@brainervirus/workit-opencode 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/assets/skills/workit-fanout/SKILL.md +1 -1
- package/assets/skills/workit-fanout/references/brief.md +4 -4
- package/assets/skills/workit-implement/SKILL.md +5 -5
- package/assets/skills/workit-review/SKILL.md +5 -0
- package/assets/skills/workit-ship/SKILL.md +1 -2
- package/dist/plugin.js +1444 -351
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -5,8 +5,8 @@ function define(plugin) {
|
|
|
5
5
|
|
|
6
6
|
// packages/workit-core/src/core.ts
|
|
7
7
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
8
|
-
import { existsSync as
|
|
9
|
-
import
|
|
8
|
+
import { existsSync as existsSync9, realpathSync as realpathSync5 } from "node:fs";
|
|
9
|
+
import path26 from "node:path";
|
|
10
10
|
|
|
11
11
|
// packages/workit-core/src/core/task-contract.ts
|
|
12
12
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -8430,7 +8430,7 @@ var workspaceConfigSchema = object({
|
|
|
8430
8430
|
}).passthrough().optional(),
|
|
8431
8431
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
8432
8432
|
defaultEndpoint: unknown().optional(),
|
|
8433
|
-
verification: _enum(["self", "independent"]).optional(),
|
|
8433
|
+
verification: _enum(["proportional", "self", "independent"]).optional(),
|
|
8434
8434
|
delegation: unknown().optional(),
|
|
8435
8435
|
trailers: boolean2().optional(),
|
|
8436
8436
|
liveSessionGuard: boolean2().optional(),
|
|
@@ -9015,6 +9015,144 @@ function resolveRef(cwd, ref) {
|
|
|
9015
9015
|
return null;
|
|
9016
9016
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
9017
9017
|
}
|
|
9018
|
+
function originHeadRef(cwd) {
|
|
9019
|
+
return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
|
|
9020
|
+
}
|
|
9021
|
+
function ownChangeDrift(cwd, range) {
|
|
9022
|
+
const before = diffRaw(cwd, range.fromFork, range.from);
|
|
9023
|
+
const after = diffRaw(cwd, range.toFork, range.to);
|
|
9024
|
+
if (!before || !after)
|
|
9025
|
+
return null;
|
|
9026
|
+
const judged = new Map(before.map((entry) => [entry.path, entry]));
|
|
9027
|
+
const current = new Map(after.map((entry) => [entry.path, entry]));
|
|
9028
|
+
const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
|
|
9029
|
+
return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
|
|
9030
|
+
}
|
|
9031
|
+
function sameOwnChange(cwd, range) {
|
|
9032
|
+
const fromFork = mergeBase(cwd, range.fromBase, range.from);
|
|
9033
|
+
const toFork = mergeBase(cwd, range.toBase, range.to);
|
|
9034
|
+
if (!fromFork || !toFork)
|
|
9035
|
+
return false;
|
|
9036
|
+
return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
|
|
9037
|
+
}
|
|
9038
|
+
var LANE_DIFF = [
|
|
9039
|
+
"-c",
|
|
9040
|
+
"core.quotePath=false",
|
|
9041
|
+
"diff",
|
|
9042
|
+
"--no-color",
|
|
9043
|
+
"--no-ext-diff",
|
|
9044
|
+
"--no-textconv",
|
|
9045
|
+
"--no-renames"
|
|
9046
|
+
];
|
|
9047
|
+
var laneRange = (fork, head) => {
|
|
9048
|
+
if (!safeArg(fork) || head !== null && !safeArg(head))
|
|
9049
|
+
return null;
|
|
9050
|
+
return head === null ? [fork] : [fork, head];
|
|
9051
|
+
};
|
|
9052
|
+
function diffNumstat(cwd, fork, head) {
|
|
9053
|
+
const range = laneRange(fork, head);
|
|
9054
|
+
if (!range)
|
|
9055
|
+
return null;
|
|
9056
|
+
const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
|
|
9057
|
+
if (!run.ok)
|
|
9058
|
+
return null;
|
|
9059
|
+
const out = [];
|
|
9060
|
+
for (const record of run.stdout.split("\x00")) {
|
|
9061
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
|
|
9062
|
+
if (!match)
|
|
9063
|
+
continue;
|
|
9064
|
+
const binary = match[1] === "-";
|
|
9065
|
+
out.push({
|
|
9066
|
+
path: match[3],
|
|
9067
|
+
added: binary ? 0 : Number(match[1]),
|
|
9068
|
+
deleted: binary ? 0 : Number(match[2]),
|
|
9069
|
+
binary
|
|
9070
|
+
});
|
|
9071
|
+
}
|
|
9072
|
+
return out;
|
|
9073
|
+
}
|
|
9074
|
+
function diffRaw(cwd, fork, head) {
|
|
9075
|
+
const range = laneRange(fork, head);
|
|
9076
|
+
if (!range)
|
|
9077
|
+
return null;
|
|
9078
|
+
const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
|
|
9079
|
+
if (!run.ok)
|
|
9080
|
+
return null;
|
|
9081
|
+
const fields = run.stdout.split("\x00");
|
|
9082
|
+
const out = [];
|
|
9083
|
+
for (let index = 0;index + 1 < fields.length; index += 2) {
|
|
9084
|
+
const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
|
|
9085
|
+
if (!meta)
|
|
9086
|
+
return null;
|
|
9087
|
+
out.push({
|
|
9088
|
+
path: fields[index + 1],
|
|
9089
|
+
oldMode: meta[1],
|
|
9090
|
+
newMode: meta[2],
|
|
9091
|
+
oldBlob: meta[3],
|
|
9092
|
+
newBlob: meta[4]
|
|
9093
|
+
});
|
|
9094
|
+
}
|
|
9095
|
+
return out;
|
|
9096
|
+
}
|
|
9097
|
+
function readBlobs(cwd, ids) {
|
|
9098
|
+
const out = new Map;
|
|
9099
|
+
const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
|
|
9100
|
+
if (!wanted.length)
|
|
9101
|
+
return out;
|
|
9102
|
+
const run = spawnSync3("git", ["cat-file", "--batch"], {
|
|
9103
|
+
cwd,
|
|
9104
|
+
input: `${wanted.join(`
|
|
9105
|
+
`)}
|
|
9106
|
+
`,
|
|
9107
|
+
maxBuffer: MAX_BUFFER,
|
|
9108
|
+
timeout: GIT_TIMEOUTS.local,
|
|
9109
|
+
killSignal: "SIGKILL",
|
|
9110
|
+
windowsHide: true
|
|
9111
|
+
});
|
|
9112
|
+
if (run.status !== 0 || !run.stdout)
|
|
9113
|
+
return null;
|
|
9114
|
+
const bytes = run.stdout;
|
|
9115
|
+
let at = 0;
|
|
9116
|
+
for (const id of wanted) {
|
|
9117
|
+
const end = bytes.indexOf(10, at);
|
|
9118
|
+
if (end < 0)
|
|
9119
|
+
return null;
|
|
9120
|
+
const header = bytes.subarray(at, end).toString("utf8").split(" ");
|
|
9121
|
+
at = end + 1;
|
|
9122
|
+
if (header[1] === "missing")
|
|
9123
|
+
continue;
|
|
9124
|
+
const size = Number(header[2]);
|
|
9125
|
+
if (header[0] !== id || !Number.isSafeInteger(size))
|
|
9126
|
+
return null;
|
|
9127
|
+
out.set(id, bytes.subarray(at, at + size));
|
|
9128
|
+
at += size + 1;
|
|
9129
|
+
}
|
|
9130
|
+
return out;
|
|
9131
|
+
}
|
|
9132
|
+
function treeFilesNamed(cwd, rev, name) {
|
|
9133
|
+
if (!safeArg(rev))
|
|
9134
|
+
return null;
|
|
9135
|
+
const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
|
|
9136
|
+
if (!run.ok)
|
|
9137
|
+
return null;
|
|
9138
|
+
const out = new Map;
|
|
9139
|
+
for (const record of run.stdout.split("\x00")) {
|
|
9140
|
+
const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
|
|
9141
|
+
if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
|
|
9142
|
+
out.set(match[2], match[1]);
|
|
9143
|
+
}
|
|
9144
|
+
return out;
|
|
9145
|
+
}
|
|
9146
|
+
function fileAt(cwd, rev, file) {
|
|
9147
|
+
if (!safeArg(rev))
|
|
9148
|
+
return null;
|
|
9149
|
+
const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
|
|
9150
|
+
return run.ok ? run.stdout : null;
|
|
9151
|
+
}
|
|
9152
|
+
function untrackedFiles(cwd) {
|
|
9153
|
+
const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
|
|
9154
|
+
return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
|
|
9155
|
+
}
|
|
9018
9156
|
var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
9019
9157
|
function currentBranch(cwd) {
|
|
9020
9158
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
@@ -12777,7 +12915,7 @@ var holdsLiveState = (state, dirty) => state === "running" || dirty === true;
|
|
|
12777
12915
|
import { createHash as createHash12 } from "node:crypto";
|
|
12778
12916
|
import * as fs14 from "node:fs";
|
|
12779
12917
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
12780
|
-
import
|
|
12918
|
+
import path18 from "node:path";
|
|
12781
12919
|
|
|
12782
12920
|
// packages/workit-core/src/check-config.ts
|
|
12783
12921
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -13007,45 +13145,786 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
13007
13145
|
}
|
|
13008
13146
|
|
|
13009
13147
|
// packages/workit-core/src/core/policy/derive.ts
|
|
13010
|
-
import { existsSync as
|
|
13011
|
-
import
|
|
13148
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
13149
|
+
import path17 from "node:path";
|
|
13012
13150
|
|
|
13013
13151
|
// packages/workit-core/src/ledger.ts
|
|
13014
13152
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
13015
13153
|
import { createHash as createHash11, randomBytes as randomBytes3 } from "node:crypto";
|
|
13016
13154
|
import fs13 from "node:fs";
|
|
13017
|
-
import
|
|
13155
|
+
import path16 from "node:path";
|
|
13156
|
+
|
|
13157
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
13158
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
13159
|
+
import path14 from "node:path";
|
|
13160
|
+
|
|
13161
|
+
// packages/workit-core/src/git/default-base.ts
|
|
13162
|
+
var safeRef = (value) => value.length > 0 && !value.startsWith("-");
|
|
13163
|
+
var trackTrunk = (cwd) => {
|
|
13164
|
+
try {
|
|
13165
|
+
const resolved = vcsConfig("resolve", cwd);
|
|
13166
|
+
if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
|
|
13167
|
+
return null;
|
|
13168
|
+
const target = String(resolved.defaultTargetBranch ?? "").trim();
|
|
13169
|
+
return target && safeRef(target) ? target : null;
|
|
13170
|
+
} catch {
|
|
13171
|
+
return null;
|
|
13172
|
+
}
|
|
13173
|
+
};
|
|
13174
|
+
function defaultBase(cwd) {
|
|
13175
|
+
const trunk = trackTrunk(cwd);
|
|
13176
|
+
if (trunk) {
|
|
13177
|
+
for (const candidate of [`origin/${trunk}`, trunk])
|
|
13178
|
+
if (resolveRef(cwd, candidate))
|
|
13179
|
+
return candidate;
|
|
13180
|
+
}
|
|
13181
|
+
const originHead = originHeadRef(cwd);
|
|
13182
|
+
if (originHead && resolveRef(cwd, originHead))
|
|
13183
|
+
return originHead;
|
|
13184
|
+
for (const candidate of [
|
|
13185
|
+
"origin/main",
|
|
13186
|
+
"origin/master",
|
|
13187
|
+
"main",
|
|
13188
|
+
"master",
|
|
13189
|
+
"origin/develop",
|
|
13190
|
+
"develop"
|
|
13191
|
+
])
|
|
13192
|
+
if (resolveRef(cwd, candidate))
|
|
13193
|
+
return candidate;
|
|
13194
|
+
return null;
|
|
13195
|
+
}
|
|
13196
|
+
|
|
13197
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
13198
|
+
function patternRegex(dir, pattern) {
|
|
13199
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
13200
|
+
return null;
|
|
13201
|
+
const anchored = pattern.includes("/");
|
|
13202
|
+
const body = pattern.replace(/^\//u, "");
|
|
13203
|
+
let source = "";
|
|
13204
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
13205
|
+
const char = body[index];
|
|
13206
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
13207
|
+
const slash = body[index + 2] === "/";
|
|
13208
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
13209
|
+
index += slash ? 2 : 1;
|
|
13210
|
+
} else if (char === "*")
|
|
13211
|
+
source += "[^/]*";
|
|
13212
|
+
else if (char === "?")
|
|
13213
|
+
source += "[^/]";
|
|
13214
|
+
else if (char === "[") {
|
|
13215
|
+
const end = body.indexOf("]", index + 2);
|
|
13216
|
+
if (end < 0)
|
|
13217
|
+
return null;
|
|
13218
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
13219
|
+
source += `[${set}]`;
|
|
13220
|
+
index = end;
|
|
13221
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
13222
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
13223
|
+
index += 1;
|
|
13224
|
+
} else
|
|
13225
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
13226
|
+
}
|
|
13227
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
13228
|
+
try {
|
|
13229
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
13230
|
+
} catch {
|
|
13231
|
+
return null;
|
|
13232
|
+
}
|
|
13233
|
+
}
|
|
13234
|
+
function rulesOf(dir, text) {
|
|
13235
|
+
const out = [];
|
|
13236
|
+
for (const raw of text.split(`
|
|
13237
|
+
`)) {
|
|
13238
|
+
const line = raw.trim();
|
|
13239
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
13240
|
+
continue;
|
|
13241
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
13242
|
+
let generated = null;
|
|
13243
|
+
for (const attribute of attributes) {
|
|
13244
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
13245
|
+
generated = true;
|
|
13246
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
13247
|
+
generated = false;
|
|
13248
|
+
}
|
|
13249
|
+
if (generated === null)
|
|
13250
|
+
continue;
|
|
13251
|
+
const regex = patternRegex(dir, pattern);
|
|
13252
|
+
if (regex)
|
|
13253
|
+
out.push({ regex, generated });
|
|
13254
|
+
}
|
|
13255
|
+
return out;
|
|
13256
|
+
}
|
|
13257
|
+
function generatedByAttributes(files, paths) {
|
|
13258
|
+
const rules = [...files].map(([file, text]) => {
|
|
13259
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
13260
|
+
return {
|
|
13261
|
+
depth: dir ? dir.split("/").length : 0,
|
|
13262
|
+
rules: rulesOf(dir, text)
|
|
13263
|
+
};
|
|
13264
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
13265
|
+
const out = new Set;
|
|
13266
|
+
for (const file of paths) {
|
|
13267
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
13268
|
+
if (last?.generated)
|
|
13269
|
+
out.add(file);
|
|
13270
|
+
}
|
|
13271
|
+
return out;
|
|
13272
|
+
}
|
|
13273
|
+
|
|
13274
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
13275
|
+
var SLASH_BLOCK = [["/*", "*/"]];
|
|
13276
|
+
var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
13277
|
+
var SYNTAXES = Object.fromEntries([
|
|
13278
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
13279
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
13280
|
+
ext,
|
|
13281
|
+
{ ...C_LIKE, triple: true }
|
|
13282
|
+
]),
|
|
13283
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
13284
|
+
...["css", "scss", "less"].map((ext) => [
|
|
13285
|
+
ext,
|
|
13286
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
13287
|
+
]),
|
|
13288
|
+
...["py", "pyi"].map((ext) => [
|
|
13289
|
+
ext,
|
|
13290
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
13291
|
+
]),
|
|
13292
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
13293
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
13294
|
+
ext,
|
|
13295
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
13296
|
+
])
|
|
13297
|
+
]);
|
|
13298
|
+
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;
|
|
13299
|
+
var extensionOf = (file) => {
|
|
13300
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
13301
|
+
const dot = name.lastIndexOf(".");
|
|
13302
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
13303
|
+
};
|
|
13304
|
+
var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
13305
|
+
var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
|
|
13306
|
+
var SPACE = /[ \t\r\f\v]/u;
|
|
13307
|
+
var WORD = /[\w$]/u;
|
|
13308
|
+
var NO_SPACE_AFTER = "([{,;";
|
|
13309
|
+
var NO_SPACE_BEFORE = ")]},;";
|
|
13310
|
+
var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
|
|
13311
|
+
var REGEX_KEYWORDS = new Set([
|
|
13312
|
+
"return",
|
|
13313
|
+
"typeof",
|
|
13314
|
+
"instanceof",
|
|
13315
|
+
"case",
|
|
13316
|
+
"do",
|
|
13317
|
+
"else",
|
|
13318
|
+
"in",
|
|
13319
|
+
"of",
|
|
13320
|
+
"new",
|
|
13321
|
+
"delete",
|
|
13322
|
+
"void",
|
|
13323
|
+
"throw",
|
|
13324
|
+
"yield",
|
|
13325
|
+
"await"
|
|
13326
|
+
]);
|
|
13327
|
+
function substanceOf(file, text) {
|
|
13328
|
+
const syntax = syntaxOf(file);
|
|
13329
|
+
if (!syntax)
|
|
13330
|
+
return plainSubstance(text);
|
|
13331
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
13332
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
13333
|
+
return plainSubstance(text);
|
|
13334
|
+
return lex(syntax, text);
|
|
13335
|
+
}
|
|
13336
|
+
function plainSubstance(text) {
|
|
13337
|
+
return text.split(`
|
|
13338
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
13339
|
+
`);
|
|
13340
|
+
}
|
|
13341
|
+
function lex(syntax, text) {
|
|
13342
|
+
const collapse = !syntax.indented;
|
|
13343
|
+
let out = "";
|
|
13344
|
+
let lineStart = 0;
|
|
13345
|
+
let kept = 0;
|
|
13346
|
+
let pendingSpace = false;
|
|
13347
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
13348
|
+
let index = 0;
|
|
13349
|
+
if (text.startsWith("#!")) {
|
|
13350
|
+
const end = text.indexOf(`
|
|
13351
|
+
`);
|
|
13352
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
13353
|
+
kept = out.length;
|
|
13354
|
+
index = end < 0 ? text.length : end;
|
|
13355
|
+
}
|
|
13356
|
+
const emitCode = (chunk) => {
|
|
13357
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
13358
|
+
const before = out[out.length - 1];
|
|
13359
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
13360
|
+
out += " ";
|
|
13361
|
+
}
|
|
13362
|
+
pendingSpace = false;
|
|
13363
|
+
out += chunk;
|
|
13364
|
+
};
|
|
13365
|
+
const emitVerbatim = (chunk) => {
|
|
13366
|
+
emitCode(chunk);
|
|
13367
|
+
kept = out.length;
|
|
13368
|
+
};
|
|
13369
|
+
const newline = () => {
|
|
13370
|
+
if (!collapse) {
|
|
13371
|
+
let end = out.length;
|
|
13372
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
13373
|
+
end -= 1;
|
|
13374
|
+
out = out.slice(0, end);
|
|
13375
|
+
}
|
|
13376
|
+
pendingSpace = false;
|
|
13377
|
+
if (out.length > lineStart) {
|
|
13378
|
+
out += `
|
|
13379
|
+
`;
|
|
13380
|
+
lineStart = out.length;
|
|
13381
|
+
}
|
|
13382
|
+
};
|
|
13383
|
+
const regexAllowed = () => {
|
|
13384
|
+
let end = out.length;
|
|
13385
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
13386
|
+
end -= 1;
|
|
13387
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
13388
|
+
return true;
|
|
13389
|
+
let start = end;
|
|
13390
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
13391
|
+
start -= 1;
|
|
13392
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
13393
|
+
};
|
|
13394
|
+
const scanString = (quote, escapes, multiline) => {
|
|
13395
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
13396
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
13397
|
+
let end = index + quote.length;
|
|
13398
|
+
while (end < text.length) {
|
|
13399
|
+
const char = text[end];
|
|
13400
|
+
if (escapes && char === "\\") {
|
|
13401
|
+
end += 2;
|
|
13402
|
+
continue;
|
|
13403
|
+
}
|
|
13404
|
+
if (text.startsWith(quote, end)) {
|
|
13405
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
13406
|
+
return false;
|
|
13407
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
13408
|
+
index = end + quote.length;
|
|
13409
|
+
return true;
|
|
13410
|
+
}
|
|
13411
|
+
if (char === `
|
|
13412
|
+
` && !multiline)
|
|
13413
|
+
return false;
|
|
13414
|
+
end += 1;
|
|
13415
|
+
}
|
|
13416
|
+
return false;
|
|
13417
|
+
};
|
|
13418
|
+
while (index < text.length) {
|
|
13419
|
+
const frame = frames[frames.length - 1];
|
|
13420
|
+
const char = text[index];
|
|
13421
|
+
if (frame.kind === "template") {
|
|
13422
|
+
if (char === "\\") {
|
|
13423
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
13424
|
+
index += 2;
|
|
13425
|
+
} else if (char === "`") {
|
|
13426
|
+
emitVerbatim(char);
|
|
13427
|
+
frames.pop();
|
|
13428
|
+
index += 1;
|
|
13429
|
+
} else if (text.startsWith("${", index)) {
|
|
13430
|
+
emitVerbatim("${");
|
|
13431
|
+
frames.push({ kind: "code", braces: 0 });
|
|
13432
|
+
index += 2;
|
|
13433
|
+
} else {
|
|
13434
|
+
out += char;
|
|
13435
|
+
kept = out.length;
|
|
13436
|
+
index += 1;
|
|
13437
|
+
}
|
|
13438
|
+
continue;
|
|
13439
|
+
}
|
|
13440
|
+
if (char === `
|
|
13441
|
+
`) {
|
|
13442
|
+
newline();
|
|
13443
|
+
index += 1;
|
|
13444
|
+
continue;
|
|
13445
|
+
}
|
|
13446
|
+
if (SPACE.test(char)) {
|
|
13447
|
+
if (collapse)
|
|
13448
|
+
pendingSpace = true;
|
|
13449
|
+
else
|
|
13450
|
+
out += char;
|
|
13451
|
+
index += 1;
|
|
13452
|
+
continue;
|
|
13453
|
+
}
|
|
13454
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
13455
|
+
if (lineOpener) {
|
|
13456
|
+
const end = text.indexOf(`
|
|
13457
|
+
`, index);
|
|
13458
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
13459
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
13460
|
+
emitVerbatim(comment);
|
|
13461
|
+
else
|
|
13462
|
+
pendingSpace = true;
|
|
13463
|
+
index = end < 0 ? text.length : end;
|
|
13464
|
+
continue;
|
|
13465
|
+
}
|
|
13466
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
13467
|
+
if (block) {
|
|
13468
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
13469
|
+
if (end < 0)
|
|
13470
|
+
return null;
|
|
13471
|
+
const comment = text.slice(index, end + block[1].length);
|
|
13472
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
13473
|
+
emitVerbatim(comment);
|
|
13474
|
+
else
|
|
13475
|
+
pendingSpace = true;
|
|
13476
|
+
if (comment.includes(`
|
|
13477
|
+
`))
|
|
13478
|
+
newline();
|
|
13479
|
+
index = end + block[1].length;
|
|
13480
|
+
continue;
|
|
13481
|
+
}
|
|
13482
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
13483
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
13484
|
+
return null;
|
|
13485
|
+
continue;
|
|
13486
|
+
}
|
|
13487
|
+
if (syntax.quotes.includes(char)) {
|
|
13488
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
13489
|
+
return null;
|
|
13490
|
+
continue;
|
|
13491
|
+
}
|
|
13492
|
+
if (syntax.literal?.includes(char)) {
|
|
13493
|
+
if (!scanString(char, false, false))
|
|
13494
|
+
return null;
|
|
13495
|
+
continue;
|
|
13496
|
+
}
|
|
13497
|
+
if (syntax.rawBacktick && char === "`") {
|
|
13498
|
+
if (!scanString("`", false, true))
|
|
13499
|
+
return null;
|
|
13500
|
+
continue;
|
|
13501
|
+
}
|
|
13502
|
+
if (syntax.js && char === "`") {
|
|
13503
|
+
emitVerbatim(char);
|
|
13504
|
+
frames.push({ kind: "template" });
|
|
13505
|
+
index += 1;
|
|
13506
|
+
continue;
|
|
13507
|
+
}
|
|
13508
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
13509
|
+
return null;
|
|
13510
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
13511
|
+
const lineEnd = text.indexOf(`
|
|
13512
|
+
`, index);
|
|
13513
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
13514
|
+
return null;
|
|
13515
|
+
}
|
|
13516
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
13517
|
+
let end = index + 1;
|
|
13518
|
+
let inClass = false;
|
|
13519
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
13520
|
+
if (text[end] === `
|
|
13521
|
+
`)
|
|
13522
|
+
return null;
|
|
13523
|
+
if (text[end] === "\\")
|
|
13524
|
+
end += 1;
|
|
13525
|
+
else if (text[end] === "[")
|
|
13526
|
+
inClass = true;
|
|
13527
|
+
else if (text[end] === "]")
|
|
13528
|
+
inClass = false;
|
|
13529
|
+
end += 1;
|
|
13530
|
+
}
|
|
13531
|
+
if (end >= text.length)
|
|
13532
|
+
return null;
|
|
13533
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
13534
|
+
index = end + 1;
|
|
13535
|
+
continue;
|
|
13536
|
+
}
|
|
13537
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
13538
|
+
if (char === "{")
|
|
13539
|
+
frame.braces += 1;
|
|
13540
|
+
else if (char === "}") {
|
|
13541
|
+
if (frame.braces === 0) {
|
|
13542
|
+
emitVerbatim(char);
|
|
13543
|
+
frames.pop();
|
|
13544
|
+
index += 1;
|
|
13545
|
+
continue;
|
|
13546
|
+
}
|
|
13547
|
+
frame.braces -= 1;
|
|
13548
|
+
}
|
|
13549
|
+
}
|
|
13550
|
+
emitCode(char);
|
|
13551
|
+
index += 1;
|
|
13552
|
+
}
|
|
13553
|
+
if (frames.length !== 1)
|
|
13554
|
+
return null;
|
|
13555
|
+
newline();
|
|
13556
|
+
return out;
|
|
13557
|
+
}
|
|
13558
|
+
function unbalanced(body) {
|
|
13559
|
+
let depth = 0;
|
|
13560
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
13561
|
+
if (part === "{")
|
|
13562
|
+
depth += 1;
|
|
13563
|
+
else if (part === "}")
|
|
13564
|
+
depth = Math.max(0, depth - 1);
|
|
13565
|
+
}
|
|
13566
|
+
return depth > 0;
|
|
13567
|
+
}
|
|
13568
|
+
function hasOpaqueLineBreak(text) {
|
|
13569
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
13570
|
+
}
|
|
13571
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
13572
|
+
function textOf(bytes) {
|
|
13573
|
+
if (bytes.includes(0))
|
|
13574
|
+
return null;
|
|
13575
|
+
try {
|
|
13576
|
+
return decoder.decode(bytes);
|
|
13577
|
+
} catch {
|
|
13578
|
+
return null;
|
|
13579
|
+
}
|
|
13580
|
+
}
|
|
13581
|
+
function cosmeticChange(file, before, after) {
|
|
13582
|
+
const left = textOf(before);
|
|
13583
|
+
const right = textOf(after);
|
|
13584
|
+
if (left === null || right === null)
|
|
13585
|
+
return false;
|
|
13586
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
13587
|
+
return false;
|
|
13588
|
+
const a = substanceOf(file, left);
|
|
13589
|
+
return a !== null && a === substanceOf(file, right);
|
|
13590
|
+
}
|
|
13591
|
+
|
|
13592
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
13593
|
+
var LANE_THRESHOLDS = {
|
|
13594
|
+
selfMaxLines: 100,
|
|
13595
|
+
selfMaxFiles: 5,
|
|
13596
|
+
fullMinLines: 500
|
|
13597
|
+
};
|
|
13598
|
+
var RISK_TOKENS = new Set([
|
|
13599
|
+
"auth",
|
|
13600
|
+
"authn",
|
|
13601
|
+
"authz",
|
|
13602
|
+
"oauth",
|
|
13603
|
+
"security",
|
|
13604
|
+
"permission",
|
|
13605
|
+
"permissions",
|
|
13606
|
+
"secret",
|
|
13607
|
+
"secrets",
|
|
13608
|
+
"crypto",
|
|
13609
|
+
"gate",
|
|
13610
|
+
"gates",
|
|
13611
|
+
"policy",
|
|
13612
|
+
"policies",
|
|
13613
|
+
"hook",
|
|
13614
|
+
"hooks",
|
|
13615
|
+
"husky",
|
|
13616
|
+
"lefthook",
|
|
13617
|
+
"release",
|
|
13618
|
+
"releases",
|
|
13619
|
+
"migration",
|
|
13620
|
+
"migrations",
|
|
13621
|
+
"migrate",
|
|
13622
|
+
"workflows"
|
|
13623
|
+
]);
|
|
13624
|
+
var RISK_FILES = new Set([
|
|
13625
|
+
".gitlab-ci.yml",
|
|
13626
|
+
"Jenkinsfile",
|
|
13627
|
+
"azure-pipelines.yml",
|
|
13628
|
+
".pre-commit-config.yaml",
|
|
13629
|
+
".workit/risk-paths"
|
|
13630
|
+
]);
|
|
13631
|
+
var RISK_NAMES = new Set([".gitattributes"]);
|
|
13632
|
+
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
13633
|
+
var LOCKFILES = new Set([
|
|
13634
|
+
"bun.lock",
|
|
13635
|
+
"bun.lockb",
|
|
13636
|
+
"package-lock.json",
|
|
13637
|
+
"npm-shrinkwrap.json",
|
|
13638
|
+
"yarn.lock",
|
|
13639
|
+
"pnpm-lock.yaml",
|
|
13640
|
+
"Cargo.lock",
|
|
13641
|
+
"go.sum",
|
|
13642
|
+
"poetry.lock",
|
|
13643
|
+
"uv.lock",
|
|
13644
|
+
"Pipfile.lock",
|
|
13645
|
+
"Gemfile.lock",
|
|
13646
|
+
"composer.lock"
|
|
13647
|
+
]);
|
|
13648
|
+
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
|
|
13649
|
+
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
13650
|
+
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
13651
|
+
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
|
|
13652
|
+
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
13653
|
+
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
13654
|
+
function kindOf(file) {
|
|
13655
|
+
if (file.binary)
|
|
13656
|
+
return "behavior";
|
|
13657
|
+
if (file.generated)
|
|
13658
|
+
return "generated";
|
|
13659
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
13660
|
+
return "lockfile";
|
|
13661
|
+
if (isDoc(file.path))
|
|
13662
|
+
return "doc";
|
|
13663
|
+
if (file.cosmetic)
|
|
13664
|
+
return "cosmetic";
|
|
13665
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
13666
|
+
}
|
|
13667
|
+
var globCache = new Map;
|
|
13668
|
+
function globMatch(pattern, file) {
|
|
13669
|
+
let regex = globCache.get(pattern);
|
|
13670
|
+
if (!regex) {
|
|
13671
|
+
let source = "";
|
|
13672
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
13673
|
+
const char = pattern[index];
|
|
13674
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
13675
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
13676
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
13677
|
+
} else if (char === "*")
|
|
13678
|
+
source += "[^/]*";
|
|
13679
|
+
else if (char === "?")
|
|
13680
|
+
source += "[^/]";
|
|
13681
|
+
else
|
|
13682
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
13683
|
+
}
|
|
13684
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
13685
|
+
globCache.set(pattern, regex);
|
|
13686
|
+
}
|
|
13687
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
13688
|
+
}
|
|
13689
|
+
function isRiskPath(file, extra = []) {
|
|
13690
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
13691
|
+
return true;
|
|
13692
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
13693
|
+
return true;
|
|
13694
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
13695
|
+
return false;
|
|
13696
|
+
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)));
|
|
13697
|
+
}
|
|
13698
|
+
var parseRiskPaths = (text) => text.split(`
|
|
13699
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
|
|
13700
|
+
function computeLane(input) {
|
|
13701
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
13702
|
+
const risky = new Set(riskFiles);
|
|
13703
|
+
const kinds = input.files.map((file) => ({
|
|
13704
|
+
file,
|
|
13705
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
13706
|
+
}));
|
|
13707
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
13708
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
13709
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
13710
|
+
const result = (lane, reasons) => ({
|
|
13711
|
+
lane,
|
|
13712
|
+
reasons,
|
|
13713
|
+
lines,
|
|
13714
|
+
files: behavior.length,
|
|
13715
|
+
riskFiles
|
|
13716
|
+
});
|
|
13717
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
13718
|
+
if (input.riskTier === "high")
|
|
13719
|
+
return result("full", ["the task judgment records high risk"]);
|
|
13720
|
+
if (riskFiles.length)
|
|
13721
|
+
return result("full", [
|
|
13722
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
13723
|
+
]);
|
|
13724
|
+
if (!behavior.length)
|
|
13725
|
+
return result("none", [
|
|
13726
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
13727
|
+
]);
|
|
13728
|
+
if (lines >= fullMinLines)
|
|
13729
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
13730
|
+
if (lines > selfMaxLines)
|
|
13731
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
13732
|
+
if (behavior.length > selfMaxFiles)
|
|
13733
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
13734
|
+
if (opaque.length)
|
|
13735
|
+
return result("focused", [
|
|
13736
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
13737
|
+
]);
|
|
13738
|
+
return result("self", [
|
|
13739
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
13740
|
+
]);
|
|
13741
|
+
}
|
|
13742
|
+
function untrackedStat(cwd, file) {
|
|
13743
|
+
try {
|
|
13744
|
+
const bytes = readFileSync4(path14.join(cwd, file));
|
|
13745
|
+
if (bytes.includes(0))
|
|
13746
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
13747
|
+
const text = bytes.toString("utf8");
|
|
13748
|
+
if (hasOpaqueLineBreak(text))
|
|
13749
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
13750
|
+
const lines = text.split(`
|
|
13751
|
+
`).length - (text.endsWith(`
|
|
13752
|
+
`) ? 1 : 0);
|
|
13753
|
+
return { path: file, added: lines, deleted: 0 };
|
|
13754
|
+
} catch {
|
|
13755
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
13756
|
+
}
|
|
13757
|
+
}
|
|
13758
|
+
var REGULAR = new Set(["100644", "100755"]);
|
|
13759
|
+
var ABSENT = /^0+$/u;
|
|
13760
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
13761
|
+
const raw = diffRaw(cwd, fork, head);
|
|
13762
|
+
if (!raw)
|
|
13763
|
+
return null;
|
|
13764
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
13765
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
13766
|
+
if (!blobs)
|
|
13767
|
+
return null;
|
|
13768
|
+
const out = new Set;
|
|
13769
|
+
const opaque = new Set;
|
|
13770
|
+
for (const entry of candidates) {
|
|
13771
|
+
const before = blobs.get(entry.oldBlob);
|
|
13772
|
+
let after = blobs.get(entry.newBlob);
|
|
13773
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
13774
|
+
try {
|
|
13775
|
+
after = readFileSync4(path14.join(cwd, entry.path));
|
|
13776
|
+
} catch {
|
|
13777
|
+
after = undefined;
|
|
13778
|
+
}
|
|
13779
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
13780
|
+
out.add(entry.path);
|
|
13781
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
13782
|
+
opaque.add(entry.path);
|
|
13783
|
+
}
|
|
13784
|
+
return { cosmetic: out, opaque };
|
|
13785
|
+
}
|
|
13786
|
+
function generatedAt(cwd, fork, paths) {
|
|
13787
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
13788
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
13789
|
+
if (!files || !blobs)
|
|
13790
|
+
return new Set;
|
|
13791
|
+
const texts = new Map;
|
|
13792
|
+
for (const [file, id] of files) {
|
|
13793
|
+
const bytes = blobs.get(id);
|
|
13794
|
+
if (bytes)
|
|
13795
|
+
texts.set(file, bytes.toString("utf8"));
|
|
13796
|
+
}
|
|
13797
|
+
return generatedByAttributes(texts, paths);
|
|
13798
|
+
}
|
|
13799
|
+
var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
|
|
13800
|
+
function laneFiles(cwd, fork, head, attributesAt = fork) {
|
|
13801
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
13802
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
13803
|
+
if (!stats || !cosmetic)
|
|
13804
|
+
return null;
|
|
13805
|
+
const files = [
|
|
13806
|
+
...stats.map((entry) => ({
|
|
13807
|
+
...entry,
|
|
13808
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
13809
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
13810
|
+
})),
|
|
13811
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
13812
|
+
];
|
|
13813
|
+
const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
|
|
13814
|
+
return files.map((entry) => ({
|
|
13815
|
+
...entry,
|
|
13816
|
+
generated: generated.has(entry.path)
|
|
13817
|
+
}));
|
|
13818
|
+
}
|
|
13819
|
+
function driftFiles(cwd, range) {
|
|
13820
|
+
if (range.fromFork === range.toFork)
|
|
13821
|
+
return laneFiles(cwd, range.from, range.to, range.toFork);
|
|
13822
|
+
const drifted = ownChangeDrift(cwd, range);
|
|
13823
|
+
const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
|
|
13824
|
+
if (!drifted || !stats)
|
|
13825
|
+
return null;
|
|
13826
|
+
const byPath = new Map(stats.map((entry) => [entry.path, entry]));
|
|
13827
|
+
const generated = generatedAt(cwd, range.toFork, drifted);
|
|
13828
|
+
return drifted.map((file) => ({
|
|
13829
|
+
path: file,
|
|
13830
|
+
added: byPath.get(file)?.added ?? 0,
|
|
13831
|
+
deleted: byPath.get(file)?.deleted ?? 0,
|
|
13832
|
+
...byPath.get(file)?.binary ? { binary: true } : {},
|
|
13833
|
+
cosmetic: false,
|
|
13834
|
+
generated: generated.has(file)
|
|
13835
|
+
}));
|
|
13836
|
+
}
|
|
13837
|
+
function branchLane(cwd, options) {
|
|
13838
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
13839
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
13840
|
+
const head = options.workingTree ? null : options.head;
|
|
13841
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
13842
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
13843
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
13844
|
+
if (!fork || !files) {
|
|
13845
|
+
const high = riskTier === "high";
|
|
13846
|
+
return {
|
|
13847
|
+
lane: high ? "full" : "focused",
|
|
13848
|
+
reasons: [
|
|
13849
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
13850
|
+
...high ? ["the task judgment records high risk"] : []
|
|
13851
|
+
],
|
|
13852
|
+
lines: 0,
|
|
13853
|
+
files: 0,
|
|
13854
|
+
riskFiles: [],
|
|
13855
|
+
base,
|
|
13856
|
+
head: options.head
|
|
13857
|
+
};
|
|
13858
|
+
}
|
|
13859
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
13860
|
+
return {
|
|
13861
|
+
...computeLane({
|
|
13862
|
+
files,
|
|
13863
|
+
riskTier,
|
|
13864
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
13865
|
+
}),
|
|
13866
|
+
base,
|
|
13867
|
+
head: options.head
|
|
13868
|
+
};
|
|
13869
|
+
}
|
|
13870
|
+
var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
13871
|
+
|
|
13872
|
+
// packages/workit-core/src/core/policy/carry.ts
|
|
13873
|
+
var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
|
|
13874
|
+
function verdictDrift(cwd, input) {
|
|
13875
|
+
const since = input.from;
|
|
13876
|
+
const stale = (reason, files = null, focused = false) => ({
|
|
13877
|
+
carries: false,
|
|
13878
|
+
since,
|
|
13879
|
+
files,
|
|
13880
|
+
focused,
|
|
13881
|
+
reason
|
|
13882
|
+
});
|
|
13883
|
+
const fromFork = mergeBase(cwd, input.fromBase, input.from);
|
|
13884
|
+
const toFork = mergeBase(cwd, input.toBase, input.to);
|
|
13885
|
+
const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
|
|
13886
|
+
if (!fromFork || !toFork || !files)
|
|
13887
|
+
return stale(`the change since ${since.slice(0, 12)} cannot be read`);
|
|
13888
|
+
const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
|
|
13889
|
+
const lane = computeLane({ files, riskPaths });
|
|
13890
|
+
const paths = files.map((file) => file.path);
|
|
13891
|
+
if (lane.lane === "none")
|
|
13892
|
+
return { carries: true, rule: "lane-none", since, files: paths };
|
|
13893
|
+
if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
|
|
13894
|
+
return { carries: true, rule: "tests-only", since, files: paths };
|
|
13895
|
+
return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
|
|
13896
|
+
}
|
|
13018
13897
|
|
|
13019
13898
|
// packages/workit-core/src/hooks/standing-signal.ts
|
|
13020
13899
|
import { createHash as createHash10 } from "node:crypto";
|
|
13021
13900
|
import fs12 from "node:fs";
|
|
13022
|
-
import
|
|
13901
|
+
import path15 from "node:path";
|
|
13023
13902
|
var gitStoreDir = (cwd) => {
|
|
13024
13903
|
try {
|
|
13025
|
-
for (let dir =
|
|
13026
|
-
const dotGit =
|
|
13904
|
+
for (let dir = path15.resolve(cwd);; dir = path15.dirname(dir)) {
|
|
13905
|
+
const dotGit = path15.join(dir, ".git");
|
|
13027
13906
|
if (fs12.existsSync(dotGit)) {
|
|
13028
13907
|
let gitDir = dotGit;
|
|
13029
13908
|
if (fs12.statSync(dotGit).isFile()) {
|
|
13030
13909
|
const target = /^gitdir:\s*(.+)$/mu.exec(fs12.readFileSync(dotGit, "utf8"))?.[1]?.trim();
|
|
13031
13910
|
if (!target)
|
|
13032
13911
|
return null;
|
|
13033
|
-
gitDir =
|
|
13034
|
-
const common =
|
|
13912
|
+
gitDir = path15.resolve(dir, target);
|
|
13913
|
+
const common = path15.join(gitDir, "commondir");
|
|
13035
13914
|
if (fs12.existsSync(common))
|
|
13036
|
-
gitDir =
|
|
13915
|
+
gitDir = path15.resolve(gitDir, fs12.readFileSync(common, "utf8").trim());
|
|
13037
13916
|
}
|
|
13038
|
-
return
|
|
13917
|
+
return path15.join(gitDir, "workit");
|
|
13039
13918
|
}
|
|
13040
|
-
if (
|
|
13919
|
+
if (path15.dirname(dir) === dir)
|
|
13041
13920
|
return null;
|
|
13042
13921
|
}
|
|
13043
13922
|
} catch {
|
|
13044
13923
|
return null;
|
|
13045
13924
|
}
|
|
13046
13925
|
};
|
|
13047
|
-
var standingRevFile = (store) =>
|
|
13048
|
-
var standingSeenFile = (store, session, agentId) =>
|
|
13926
|
+
var standingRevFile = (store) => path15.join(store, "standing", "rev");
|
|
13927
|
+
var standingSeenFile = (store, session, agentId) => path15.join(store, "standing", "seen", `${createHash10("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
|
|
13049
13928
|
var readStandingRev = (store) => {
|
|
13050
13929
|
try {
|
|
13051
13930
|
return fs12.readFileSync(standingRevFile(store), "utf8").trim() || null;
|
|
@@ -13066,7 +13945,7 @@ var readStandingSeen = (file) => {
|
|
|
13066
13945
|
};
|
|
13067
13946
|
var writeStandingSeen = (file, seen) => {
|
|
13068
13947
|
try {
|
|
13069
|
-
fs12.mkdirSync(
|
|
13948
|
+
fs12.mkdirSync(path15.dirname(file), { recursive: true });
|
|
13070
13949
|
fs12.writeFileSync(file, JSON.stringify(seen));
|
|
13071
13950
|
} catch {}
|
|
13072
13951
|
};
|
|
@@ -13116,52 +13995,19 @@ var git4 = (cwd, args) => {
|
|
|
13116
13995
|
const value = run.ok ? run.stdout.trim() : "";
|
|
13117
13996
|
return value ? value : null;
|
|
13118
13997
|
};
|
|
13119
|
-
var
|
|
13998
|
+
var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
|
|
13120
13999
|
function resolveCommit(cwd, ref) {
|
|
13121
|
-
if (!
|
|
14000
|
+
if (!safeRef2(ref))
|
|
13122
14001
|
return null;
|
|
13123
14002
|
return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
13124
14003
|
}
|
|
13125
14004
|
function branchHead(cwd, branch) {
|
|
13126
|
-
if (!
|
|
14005
|
+
if (!safeRef2(branch))
|
|
13127
14006
|
return null;
|
|
13128
14007
|
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
13129
14008
|
}
|
|
13130
|
-
var trackTrunk = (cwd) => {
|
|
13131
|
-
try {
|
|
13132
|
-
const resolved = vcsConfig("resolve", cwd);
|
|
13133
|
-
if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
|
|
13134
|
-
return null;
|
|
13135
|
-
const target = String(resolved.defaultTargetBranch ?? "").trim();
|
|
13136
|
-
return target && safeRef(target) ? target : null;
|
|
13137
|
-
} catch {
|
|
13138
|
-
return null;
|
|
13139
|
-
}
|
|
13140
|
-
};
|
|
13141
|
-
function defaultBase(cwd) {
|
|
13142
|
-
const trunk = trackTrunk(cwd);
|
|
13143
|
-
if (trunk) {
|
|
13144
|
-
for (const candidate of [`origin/${trunk}`, trunk])
|
|
13145
|
-
if (resolveCommit(cwd, candidate))
|
|
13146
|
-
return candidate;
|
|
13147
|
-
}
|
|
13148
|
-
const originHead = git4(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
|
|
13149
|
-
if (originHead && resolveCommit(cwd, originHead))
|
|
13150
|
-
return originHead;
|
|
13151
|
-
for (const candidate of [
|
|
13152
|
-
"origin/main",
|
|
13153
|
-
"origin/master",
|
|
13154
|
-
"main",
|
|
13155
|
-
"master",
|
|
13156
|
-
"origin/develop",
|
|
13157
|
-
"develop"
|
|
13158
|
-
])
|
|
13159
|
-
if (resolveCommit(cwd, candidate))
|
|
13160
|
-
return candidate;
|
|
13161
|
-
return null;
|
|
13162
|
-
}
|
|
13163
14009
|
function diffHash(cwd, base, head) {
|
|
13164
|
-
if (!
|
|
14010
|
+
if (!safeRef2(base) || !safeRef2(head))
|
|
13165
14011
|
return null;
|
|
13166
14012
|
const diff = gitRun2(cwd, [
|
|
13167
14013
|
"diff",
|
|
@@ -13193,14 +14039,14 @@ function diffHash(cwd, base, head) {
|
|
|
13193
14039
|
return `sha256:${hash.digest("hex")}`;
|
|
13194
14040
|
}
|
|
13195
14041
|
function storeRoot(cwd) {
|
|
13196
|
-
const location = resolveStore(
|
|
14042
|
+
const location = resolveStore(path16.resolve(cwd));
|
|
13197
14043
|
if (location instanceof Error)
|
|
13198
14044
|
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");
|
|
13199
14045
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
13200
14046
|
}
|
|
13201
14047
|
function ledgerPath(cwd) {
|
|
13202
14048
|
const root = storeRoot(cwd);
|
|
13203
|
-
return root.ok ? { ok: true, value:
|
|
14049
|
+
return root.ok ? { ok: true, value: path16.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
13204
14050
|
}
|
|
13205
14051
|
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
13206
14052
|
var NETWORK_FS_TYPES = new Set([
|
|
@@ -13230,13 +14076,13 @@ var LOCK_STALE_MS = 30000;
|
|
|
13230
14076
|
var LOCK_OWNER = "owner";
|
|
13231
14077
|
var lockToken = (lock) => {
|
|
13232
14078
|
try {
|
|
13233
|
-
return fs13.readFileSync(
|
|
14079
|
+
return fs13.readFileSync(path16.join(lock, LOCK_OWNER), "utf8");
|
|
13234
14080
|
} catch {
|
|
13235
14081
|
return null;
|
|
13236
14082
|
}
|
|
13237
14083
|
};
|
|
13238
14084
|
var lockAgeMs = (lock) => {
|
|
13239
|
-
for (const target of [
|
|
14085
|
+
for (const target of [path16.join(lock, LOCK_OWNER), lock]) {
|
|
13240
14086
|
try {
|
|
13241
14087
|
return Date.now() - fs13.statSync(target).mtimeMs;
|
|
13242
14088
|
} catch {}
|
|
@@ -13253,7 +14099,7 @@ var ledgerLock = {
|
|
|
13253
14099
|
throw error;
|
|
13254
14100
|
}
|
|
13255
14101
|
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
13256
|
-
fs13.writeFileSync(
|
|
14102
|
+
fs13.writeFileSync(path16.join(lock, LOCK_OWNER), token);
|
|
13257
14103
|
return token;
|
|
13258
14104
|
},
|
|
13259
14105
|
release(lock, token) {
|
|
@@ -13386,8 +14232,8 @@ function appendLine(file, build, options) {
|
|
|
13386
14232
|
}
|
|
13387
14233
|
};
|
|
13388
14234
|
try {
|
|
13389
|
-
fs13.mkdirSync(
|
|
13390
|
-
if (!options.forceLock && !onNetworkFs(
|
|
14235
|
+
fs13.mkdirSync(path16.dirname(file), { recursive: true });
|
|
14236
|
+
if (!options.forceLock && !onNetworkFs(path16.dirname(file)))
|
|
13391
14237
|
return write();
|
|
13392
14238
|
const locked = withLedgerLock(file, write);
|
|
13393
14239
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -13621,7 +14467,7 @@ function verdictBasis(verdict, current) {
|
|
|
13621
14467
|
}
|
|
13622
14468
|
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
13623
14469
|
function trailerSessions(cwd, base, head) {
|
|
13624
|
-
if (!base || !head || !
|
|
14470
|
+
if (!base || !head || !safeRef2(base) || !safeRef2(head))
|
|
13625
14471
|
return [];
|
|
13626
14472
|
const out = git4(cwd, [
|
|
13627
14473
|
"log",
|
|
@@ -13634,7 +14480,7 @@ var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
|
|
|
13634
14480
|
function branchScope(cwd, branch) {
|
|
13635
14481
|
const renamedAt = new Map;
|
|
13636
14482
|
let since = Number.NEGATIVE_INFINITY;
|
|
13637
|
-
if (
|
|
14483
|
+
if (safeRef2(branch)) {
|
|
13638
14484
|
const log = git4(cwd, [
|
|
13639
14485
|
"log",
|
|
13640
14486
|
"-g",
|
|
@@ -13779,12 +14625,29 @@ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
|
13779
14625
|
}
|
|
13780
14626
|
return effective;
|
|
13781
14627
|
}
|
|
13782
|
-
|
|
14628
|
+
var restackExact = new WeakMap;
|
|
14629
|
+
function restackCarries(exactIn, rows, branch, from, to) {
|
|
13783
14630
|
if (!from || !to)
|
|
13784
14631
|
return false;
|
|
14632
|
+
const exact = (row, fromHead, head) => {
|
|
14633
|
+
if (exactIn === null)
|
|
14634
|
+
return true;
|
|
14635
|
+
const cwd = exactIn;
|
|
14636
|
+
let known = restackExact.get(row);
|
|
14637
|
+
if (known === undefined) {
|
|
14638
|
+
known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
|
|
14639
|
+
from: fromHead,
|
|
14640
|
+
to: head,
|
|
14641
|
+
fromBase: row.fromBase,
|
|
14642
|
+
toBase: row.toBase
|
|
14643
|
+
});
|
|
14644
|
+
restackExact.set(row, known);
|
|
14645
|
+
}
|
|
14646
|
+
return known;
|
|
14647
|
+
};
|
|
13785
14648
|
const edges = new Map;
|
|
13786
14649
|
for (const row of rows)
|
|
13787
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
14650
|
+
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))
|
|
13788
14651
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
13789
14652
|
const seen = new Set([from]);
|
|
13790
14653
|
const queue = [from];
|
|
@@ -13818,9 +14681,57 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
13818
14681
|
return key;
|
|
13819
14682
|
};
|
|
13820
14683
|
const keyFor = (base) => keysAt(base, head);
|
|
13821
|
-
const
|
|
14684
|
+
const driftCache = new Map;
|
|
14685
|
+
const driftOf = (row) => {
|
|
14686
|
+
if (driftCache.has(row))
|
|
14687
|
+
return driftCache.get(row) ?? null;
|
|
14688
|
+
let drift = null;
|
|
14689
|
+
const toBase = row.base ?? fallbackBase;
|
|
14690
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
14691
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
14692
|
+
if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
|
|
14693
|
+
drift = verdictDrift(cwd, {
|
|
14694
|
+
from: row.head,
|
|
14695
|
+
to: head,
|
|
14696
|
+
fromBase,
|
|
14697
|
+
toBase: toBaseSha,
|
|
14698
|
+
kind: typeof row.kind === "string" ? row.kind : "review"
|
|
14699
|
+
});
|
|
14700
|
+
driftCache.set(row, drift);
|
|
14701
|
+
return drift;
|
|
14702
|
+
};
|
|
14703
|
+
const exactCache = new Map;
|
|
14704
|
+
const exactCarry = (row) => {
|
|
14705
|
+
if (FAILING.has(String(row.result)))
|
|
14706
|
+
return true;
|
|
14707
|
+
let exact = exactCache.get(row);
|
|
14708
|
+
if (exact === undefined) {
|
|
14709
|
+
const toBase = row.base ?? fallbackBase;
|
|
14710
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
14711
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
14712
|
+
exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
|
|
14713
|
+
exactCache.set(row, exact);
|
|
14714
|
+
}
|
|
14715
|
+
return exact;
|
|
14716
|
+
};
|
|
14717
|
+
const keyBasis = (row) => {
|
|
13822
14718
|
const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
13823
|
-
return basis === "
|
|
14719
|
+
return basis === "carried" && !exactCarry(row) ? "stale" : basis;
|
|
14720
|
+
};
|
|
14721
|
+
const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
|
|
14722
|
+
const carryOf = (row) => {
|
|
14723
|
+
if (keyBasis(row) !== "stale")
|
|
14724
|
+
return null;
|
|
14725
|
+
if (restacked(row))
|
|
14726
|
+
return null;
|
|
14727
|
+
const drift = driftOf(row);
|
|
14728
|
+
return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
|
|
14729
|
+
};
|
|
14730
|
+
const basisOf = (row) => {
|
|
14731
|
+
const basis = keyBasis(row);
|
|
14732
|
+
if (basis !== "stale")
|
|
14733
|
+
return basis;
|
|
14734
|
+
return restacked(row) || carryOf(row) ? "carried" : basis;
|
|
13824
14735
|
};
|
|
13825
14736
|
const sameCode = (failed, row) => {
|
|
13826
14737
|
if (failed.head && failed.head === row.head)
|
|
@@ -13828,7 +14739,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
13828
14739
|
const base = failed.base ?? fallbackBase;
|
|
13829
14740
|
const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
|
|
13830
14741
|
const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
|
|
13831
|
-
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
14742
|
+
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
|
|
13832
14743
|
};
|
|
13833
14744
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
13834
14745
|
const failureAuthorCache = new Map;
|
|
@@ -13884,6 +14795,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
13884
14795
|
independent: independence.length === 0,
|
|
13885
14796
|
accepted: reasons.length === 0,
|
|
13886
14797
|
reasons,
|
|
14798
|
+
carry: basis === "carried" ? carryOf(row) : null,
|
|
13887
14799
|
verdict: row
|
|
13888
14800
|
});
|
|
13889
14801
|
if (reasons.length === 0) {
|
|
@@ -13912,7 +14824,12 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
13912
14824
|
verdict: failing?.verdict ?? null,
|
|
13913
14825
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
13914
14826
|
};
|
|
13915
|
-
const
|
|
14827
|
+
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))));
|
|
14828
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
14829
|
+
const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
|
|
14830
|
+
const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
14831
|
+
const drift = lastPass ? driftOf(lastPass.verdict) : null;
|
|
14832
|
+
const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
|
|
13916
14833
|
return {
|
|
13917
14834
|
branch,
|
|
13918
14835
|
head,
|
|
@@ -13921,6 +14838,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
13921
14838
|
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
13922
14839
|
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
13923
14840
|
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
14841
|
+
carry: carryEntry?.carry ?? null,
|
|
14842
|
+
reverify,
|
|
13924
14843
|
accepted,
|
|
13925
14844
|
verdicts,
|
|
13926
14845
|
authors: [...authors].toSorted(),
|
|
@@ -13962,6 +14881,7 @@ var RULES = {
|
|
|
13962
14881
|
self: "verdict:self",
|
|
13963
14882
|
nonAuthor: "verdict:non-author",
|
|
13964
14883
|
verified: "verdict:verified",
|
|
14884
|
+
lane: "verdict:lane",
|
|
13965
14885
|
product: "decision:product",
|
|
13966
14886
|
plan: "plan"
|
|
13967
14887
|
};
|
|
@@ -13984,6 +14904,7 @@ var normalizedScope = (scope) => ({
|
|
|
13984
14904
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
13985
14905
|
const out = [];
|
|
13986
14906
|
const base = { scope, dependentAction: null };
|
|
14907
|
+
const proportional = verification === "proportional";
|
|
13987
14908
|
if (judgment.behaviorChange)
|
|
13988
14909
|
out.push(derived2({
|
|
13989
14910
|
...base,
|
|
@@ -13994,7 +14915,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
|
|
|
13994
14915
|
before: "close",
|
|
13995
14916
|
acceptanceAllowed: false
|
|
13996
14917
|
}));
|
|
13997
|
-
if (
|
|
14918
|
+
if (proportional && judgment.riskTier === "normal")
|
|
14919
|
+
out.push(derived2({
|
|
14920
|
+
...base,
|
|
14921
|
+
ruleId: RULES.lane,
|
|
14922
|
+
dimension: "review",
|
|
14923
|
+
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.",
|
|
14924
|
+
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.",
|
|
14925
|
+
before: "close",
|
|
14926
|
+
acceptanceAllowed: false
|
|
14927
|
+
}));
|
|
14928
|
+
if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
|
|
13998
14929
|
out.push(verification === "independent" ? derived2({
|
|
13999
14930
|
...base,
|
|
14000
14931
|
ruleId: RULES.nonAuthor,
|
|
@@ -14097,7 +15028,7 @@ var productDecision = (task, ledger) => {
|
|
|
14097
15028
|
};
|
|
14098
15029
|
};
|
|
14099
15030
|
var planRecorded = (task, root) => {
|
|
14100
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" &&
|
|
15031
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path17.resolve(root, ref.path)));
|
|
14101
15032
|
if (cited)
|
|
14102
15033
|
return {
|
|
14103
15034
|
met: true,
|
|
@@ -14111,14 +15042,30 @@ var planRecorded = (task, root) => {
|
|
|
14111
15042
|
decisionIds: []
|
|
14112
15043
|
};
|
|
14113
15044
|
};
|
|
14114
|
-
var
|
|
15045
|
+
var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
|
|
15046
|
+
var laneVerdict = (task, root, ledger) => {
|
|
15047
|
+
if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
|
|
15048
|
+
return {
|
|
15049
|
+
met: true,
|
|
15050
|
+
reason: "no branch to read a lane from, and the judgment records no behavior change",
|
|
15051
|
+
decisionIds: []
|
|
15052
|
+
};
|
|
15053
|
+
const lane = workingLane(task, root);
|
|
15054
|
+
const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
|
|
15055
|
+
const need = laneNeed(lane.lane);
|
|
15056
|
+
if (need === "none")
|
|
15057
|
+
return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
|
|
15058
|
+
const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
|
|
15059
|
+
return { ...status, reason: `${label}: ${status.reason}` };
|
|
15060
|
+
};
|
|
15061
|
+
var verdict = (ruleId, root, ledger) => {
|
|
14115
15062
|
const branch = ledger.branch();
|
|
14116
15063
|
if (!branch)
|
|
14117
15064
|
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
14118
15065
|
const check = checkVerdicts(root, branch, ledger.rows());
|
|
14119
15066
|
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
14120
15067
|
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
14121
|
-
const met = !failing && (
|
|
15068
|
+
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)));
|
|
14122
15069
|
if (met)
|
|
14123
15070
|
return {
|
|
14124
15071
|
met: true,
|
|
@@ -14126,7 +15073,7 @@ var verdict = (requirement, root, ledger) => {
|
|
|
14126
15073
|
decisionIds: []
|
|
14127
15074
|
};
|
|
14128
15075
|
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, " ");
|
|
14129
|
-
const unblock =
|
|
15076
|
+
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>\``;
|
|
14130
15077
|
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
14131
15078
|
};
|
|
14132
15079
|
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
@@ -14138,7 +15085,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
14138
15085
|
case RULES.self:
|
|
14139
15086
|
case RULES.nonAuthor:
|
|
14140
15087
|
case RULES.verified:
|
|
14141
|
-
return verdict(requirement, root, ledger);
|
|
15088
|
+
return verdict(requirement.ruleId, root, ledger);
|
|
15089
|
+
case RULES.lane:
|
|
15090
|
+
return laneVerdict(task, root, ledger);
|
|
14142
15091
|
default:
|
|
14143
15092
|
return null;
|
|
14144
15093
|
}
|
|
@@ -14157,14 +15106,14 @@ function writeBlockers(task, root) {
|
|
|
14157
15106
|
|
|
14158
15107
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14159
15108
|
var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
|
|
14160
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
14161
|
-
var relative = (root, candidate) =>
|
|
15109
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`);
|
|
15110
|
+
var relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".";
|
|
14162
15111
|
var canonicalPath = (value) => {
|
|
14163
15112
|
if (typeof value !== "string" || !value)
|
|
14164
15113
|
return null;
|
|
14165
15114
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14166
15115
|
return null;
|
|
14167
|
-
const normalized =
|
|
15116
|
+
const normalized = path18.posix.normalize(value);
|
|
14168
15117
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14169
15118
|
return null;
|
|
14170
15119
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14212,14 +15161,14 @@ var gitPaths = (root) => {
|
|
|
14212
15161
|
git: !/not a git repository/i.test(stderr)
|
|
14213
15162
|
};
|
|
14214
15163
|
}
|
|
14215
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15164
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
|
|
14216
15165
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14217
15166
|
if (stagedDeleted.status !== 0) {
|
|
14218
15167
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14219
15168
|
if (!/not a git repository/i.test(stderr))
|
|
14220
15169
|
return { paths, uncertain: true, git: true };
|
|
14221
15170
|
} else if (stagedDeleted.stdout) {
|
|
14222
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15171
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
|
|
14223
15172
|
}
|
|
14224
15173
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14225
15174
|
};
|
|
@@ -14234,7 +15183,7 @@ var walk = (root, directory, output) => {
|
|
|
14234
15183
|
for (const name of names) {
|
|
14235
15184
|
if (name === ".git" || name === ".workit")
|
|
14236
15185
|
continue;
|
|
14237
|
-
const target =
|
|
15186
|
+
const target = path18.join(directory, name);
|
|
14238
15187
|
let stat;
|
|
14239
15188
|
try {
|
|
14240
15189
|
stat = fs14.lstatSync(target);
|
|
@@ -14252,12 +15201,12 @@ var walk = (root, directory, output) => {
|
|
|
14252
15201
|
var scopeRoots = (root, scope) => {
|
|
14253
15202
|
const roots = [];
|
|
14254
15203
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14255
|
-
const target =
|
|
15204
|
+
const target = path18.resolve(root, item);
|
|
14256
15205
|
if (!inside(root, target))
|
|
14257
15206
|
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` }] });
|
|
14258
15207
|
let ancestor = target;
|
|
14259
15208
|
while (!fs14.existsSync(ancestor) && ancestor !== root)
|
|
14260
|
-
ancestor =
|
|
15209
|
+
ancestor = path18.dirname(ancestor);
|
|
14261
15210
|
try {
|
|
14262
15211
|
if (!inside(root, fs14.realpathSync(ancestor)))
|
|
14263
15212
|
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` }] });
|
|
@@ -14309,7 +15258,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14309
15258
|
for (const item of [...names].toSorted()) {
|
|
14310
15259
|
if (!scopeMatches(item, normalizedScope))
|
|
14311
15260
|
continue;
|
|
14312
|
-
const target =
|
|
15261
|
+
const target = path18.join(checkout, item);
|
|
14313
15262
|
try {
|
|
14314
15263
|
const stat = fs14.lstatSync(target);
|
|
14315
15264
|
if (stat.isSymbolicLink()) {
|
|
@@ -14493,23 +15442,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
14493
15442
|
continue;
|
|
14494
15443
|
if (!reference.digest)
|
|
14495
15444
|
return failure2("invalid_input", "document references require a byte digest");
|
|
14496
|
-
const target =
|
|
14497
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15445
|
+
const target = path18.resolve(root, reference.path);
|
|
15446
|
+
if (target !== root && !target.startsWith(`${root}${path18.sep}`))
|
|
14498
15447
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14499
15448
|
fields: [
|
|
14500
15449
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
14501
15450
|
]
|
|
14502
15451
|
});
|
|
14503
15452
|
try {
|
|
14504
|
-
const below =
|
|
15453
|
+
const below = path18.relative(root, target);
|
|
14505
15454
|
let current = root;
|
|
14506
|
-
for (const segment of below.split(
|
|
15455
|
+
for (const segment of below.split(path18.sep)) {
|
|
14507
15456
|
if (!segment)
|
|
14508
15457
|
continue;
|
|
14509
|
-
current =
|
|
15458
|
+
current = path18.join(current, segment);
|
|
14510
15459
|
const stat = fs14.lstatSync(current);
|
|
14511
15460
|
const real = fs14.realpathSync(current);
|
|
14512
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15461
|
+
if (real !== root && !real.startsWith(`${root}${path18.sep}`))
|
|
14513
15462
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14514
15463
|
fields: [
|
|
14515
15464
|
{
|
|
@@ -14942,9 +15891,9 @@ Autonomy contract:
|
|
|
14942
15891
|
Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
|
|
14943
15892
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
14944
15893
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
14945
|
-
self-reviewed, never verified. High risk,
|
|
14946
|
-
merge need a verifier with its
|
|
14947
|
-
\`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
15894
|
+
self-reviewed, never verified. High risk, a focused or full lane and
|
|
15895
|
+
merge under \`verification: self|independent\` need a verifier with its
|
|
15896
|
+
own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
14948
15897
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
14949
15898
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
14950
15899
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -14952,7 +15901,7 @@ Autonomy contract:
|
|
|
14952
15901
|
CLI first: if a step has one right answer, use the \`workit\` verb instead of
|
|
14953
15902
|
hand-running git, gh or glab (\`workit git|pr|ci|check|help\`;
|
|
14954
15903
|
else \`npx -y @brainervirus/workit-cli\`).
|
|
14955
|
-
Other checkout: \`workit --cwd <dir>
|
|
15904
|
+
Other checkout: \`workit <verb> --cwd <dir> …\` (verb first: host rules match it); one workit
|
|
14956
15905
|
verb per shell call, never \`cd <dir> && workit\`. zsh-safe: no \`echo =====\`
|
|
14957
15906
|
(\`printf '%s\\n' ---\`), quote globs and URLs, no variable named \`status\` or \`path\`.
|
|
14958
15907
|
Add Co-Authored-By via \`workit git commit --trailer "K: v" -- <paths>\`, never
|
|
@@ -14964,7 +15913,7 @@ authorizes an effect. Judge work once with
|
|
|
14964
15913
|
\`workit policy assess --judge risk= behavior= product-choice= plan=\`.
|
|
14965
15914
|
Across repositories, bind items to checkout, branch and endpoint, resolve
|
|
14966
15915
|
competing targets before a mutation, and reconcile an uncertain
|
|
14967
|
-
|
|
15916
|
+
effect before a retry.
|
|
14968
15917
|
|
|
14969
15918
|
Mid-task: never silently drop or resume an objective (workit-continue).
|
|
14970
15919
|
|
|
@@ -15107,20 +16056,20 @@ var exportDigest = (bundle) => sha256({
|
|
|
15107
16056
|
});
|
|
15108
16057
|
// packages/workit-core/src/core/route-intent.ts
|
|
15109
16058
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
15110
|
-
import { existsSync as
|
|
15111
|
-
import
|
|
16059
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
16060
|
+
import path21 from "node:path";
|
|
15112
16061
|
|
|
15113
16062
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
15114
16063
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
15115
16064
|
import { createHash as createHash14 } from "node:crypto";
|
|
15116
16065
|
import fs16 from "node:fs";
|
|
15117
16066
|
import os4 from "node:os";
|
|
15118
|
-
import
|
|
16067
|
+
import path20 from "node:path";
|
|
15119
16068
|
|
|
15120
16069
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
15121
16070
|
import { createHash as createHash13 } from "node:crypto";
|
|
15122
16071
|
import fs15 from "node:fs";
|
|
15123
|
-
import
|
|
16072
|
+
import path19 from "node:path";
|
|
15124
16073
|
var sessionMarkerFile = (input, session, store) => {
|
|
15125
16074
|
let dir = store;
|
|
15126
16075
|
if (dir === undefined) {
|
|
@@ -15130,7 +16079,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
15130
16079
|
dir = location.dir;
|
|
15131
16080
|
}
|
|
15132
16081
|
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
15133
|
-
return
|
|
16082
|
+
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
15134
16083
|
};
|
|
15135
16084
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
15136
16085
|
var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
|
|
@@ -15157,7 +16106,7 @@ var parseMarker = (text) => {
|
|
|
15157
16106
|
};
|
|
15158
16107
|
var readSessionMarker = (file) => parseMarker(readRaw(file));
|
|
15159
16108
|
var writeAtomic = (file, marker) => {
|
|
15160
|
-
fs15.mkdirSync(
|
|
16109
|
+
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
15161
16110
|
const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
15162
16111
|
try {
|
|
15163
16112
|
fs15.writeFileSync(temp, JSON.stringify(marker));
|
|
@@ -15434,6 +16383,18 @@ var substitutions = (body) => {
|
|
|
15434
16383
|
};
|
|
15435
16384
|
var PLAIN_POSIX = /[^\s'"\\`$(){},<>|;&#]+/uy;
|
|
15436
16385
|
var PLAIN_POWERSHELL = /[^\s'"`(){}<>|;&#]+/uy;
|
|
16386
|
+
var expandsInQuotes = (body, dialect) => {
|
|
16387
|
+
const escape = dialect === "posix" ? "\\" : "`";
|
|
16388
|
+
for (let index = 0;index < body.length; index++) {
|
|
16389
|
+
const char = body[index];
|
|
16390
|
+
if (char === escape)
|
|
16391
|
+
index++;
|
|
16392
|
+
else if (char === "$" || dialect === "posix" && char === "`")
|
|
16393
|
+
return true;
|
|
16394
|
+
}
|
|
16395
|
+
return false;
|
|
16396
|
+
};
|
|
16397
|
+
var EXPANDING_RUN = /[*?[]/u;
|
|
15437
16398
|
var arithmeticEnd = (command, start) => {
|
|
15438
16399
|
const end = substitutionEnd(command, start);
|
|
15439
16400
|
return command[end - 1] === ")" && end > start + 2 ? end : 0;
|
|
@@ -15452,6 +16413,7 @@ var tokenize = (command, dialect) => {
|
|
|
15452
16413
|
let current = { words: [], redirects: [] };
|
|
15453
16414
|
let word = "";
|
|
15454
16415
|
let inWord = false;
|
|
16416
|
+
let expanding = false;
|
|
15455
16417
|
let pending = null;
|
|
15456
16418
|
const heredocs = [];
|
|
15457
16419
|
const escape = posix ? "\\" : "`";
|
|
@@ -15466,11 +16428,16 @@ var tokenize = (command, dialect) => {
|
|
|
15466
16428
|
heredocs.push({ delimiter: text.replace(/^-/, ""), owner: current });
|
|
15467
16429
|
else if (pending === "herestring")
|
|
15468
16430
|
(current.stdin ??= []).push(text);
|
|
15469
|
-
else if (pending !== "input")
|
|
16431
|
+
else if (pending !== "input") {
|
|
16432
|
+
const at = current.words.length;
|
|
15470
16433
|
current.words.push(...MARKERS.test(word) ? expandBraces(word) : [word]);
|
|
16434
|
+
if (expanding || !posix && word.startsWith("@"))
|
|
16435
|
+
(current.expands ??= []).push(...current.words.slice(at).map((_, i) => at + i));
|
|
16436
|
+
}
|
|
15471
16437
|
pending = null;
|
|
15472
16438
|
word = "";
|
|
15473
16439
|
inWord = false;
|
|
16440
|
+
expanding = false;
|
|
15474
16441
|
};
|
|
15475
16442
|
const flushSegment = () => {
|
|
15476
16443
|
flushWord();
|
|
@@ -15485,6 +16452,7 @@ var tokenize = (command, dialect) => {
|
|
|
15485
16452
|
};
|
|
15486
16453
|
const substitution = (inner, text) => {
|
|
15487
16454
|
(current.subs ??= []).push(inner);
|
|
16455
|
+
expanding = true;
|
|
15488
16456
|
word += text;
|
|
15489
16457
|
inWord = true;
|
|
15490
16458
|
};
|
|
@@ -15495,6 +16463,8 @@ var tokenize = (command, dialect) => {
|
|
|
15495
16463
|
if (run) {
|
|
15496
16464
|
word += run[0];
|
|
15497
16465
|
inWord = true;
|
|
16466
|
+
if (EXPANDING_RUN.test(run[0]))
|
|
16467
|
+
expanding = true;
|
|
15498
16468
|
index += run[0].length - 1;
|
|
15499
16469
|
continue;
|
|
15500
16470
|
}
|
|
@@ -15523,6 +16493,8 @@ var tokenize = (command, dialect) => {
|
|
|
15523
16493
|
const body = command.slice(index + 1, end < 0 ? undefined : end);
|
|
15524
16494
|
if (posix && char === '"' && /\$\(|`/.test(body))
|
|
15525
16495
|
(current.subs ??= []).push(...substitutions(body));
|
|
16496
|
+
if (char === '"' && expandsInQuotes(body, dialect))
|
|
16497
|
+
expanding = true;
|
|
15526
16498
|
word += unquote(body, char, dialect);
|
|
15527
16499
|
inWord = true;
|
|
15528
16500
|
index = end < 0 ? command.length : end;
|
|
@@ -15568,6 +16540,8 @@ var tokenize = (command, dialect) => {
|
|
|
15568
16540
|
if (char === "(" && inWord && word.endsWith("$")) {
|
|
15569
16541
|
word = word.slice(0, -1);
|
|
15570
16542
|
inWord = word !== "";
|
|
16543
|
+
if (!inWord)
|
|
16544
|
+
expanding = false;
|
|
15571
16545
|
}
|
|
15572
16546
|
group(char === "(" ? "open" : "close");
|
|
15573
16547
|
} else if (!posix && (char === "{" || char === "}")) {
|
|
@@ -15621,6 +16595,8 @@ var tokenize = (command, dialect) => {
|
|
|
15621
16595
|
else if (posix && char === "}" && word.includes(OPEN))
|
|
15622
16596
|
word += CLOSE;
|
|
15623
16597
|
else {
|
|
16598
|
+
if (char === "$")
|
|
16599
|
+
expanding = true;
|
|
15624
16600
|
word += char;
|
|
15625
16601
|
inWord = true;
|
|
15626
16602
|
}
|
|
@@ -15678,14 +16654,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
|
|
|
15678
16654
|
var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
|
|
15679
16655
|
var MAX_DEPTH = 4;
|
|
15680
16656
|
var MAX_EXPANSIONS = 16;
|
|
15681
|
-
var isAbsolute = (value) =>
|
|
16657
|
+
var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
15682
16658
|
var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
|
|
15683
16659
|
var joinDir = (base, raw) => {
|
|
15684
16660
|
const next = raw.replace(HERE, ".");
|
|
15685
16661
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
15686
16662
|
return;
|
|
15687
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
15688
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
16663
|
+
const expanded = next === "~" || next.startsWith("~/") ? path20.join(os4.homedir(), next.slice(1)) : next;
|
|
16664
|
+
return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
|
|
15689
16665
|
};
|
|
15690
16666
|
var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
|
|
15691
16667
|
var cdTarget = (words) => {
|
|
@@ -16065,8 +17041,17 @@ function walk2(command, dialect, depth, seen) {
|
|
|
16065
17041
|
visit(next, undefined, hops + 1);
|
|
16066
17042
|
};
|
|
16067
17043
|
const wrapped = seen ? null : workitCheckTail(words);
|
|
16068
|
-
if (wrapped)
|
|
16069
|
-
|
|
17044
|
+
if (wrapped) {
|
|
17045
|
+
const outer = dir;
|
|
17046
|
+
if (wrapped.cwd !== null)
|
|
17047
|
+
dir = joinDir(dir, wrapped.cwd);
|
|
17048
|
+
if ("script" in wrapped)
|
|
17049
|
+
nested(wrapped.script, "posix");
|
|
17050
|
+
else
|
|
17051
|
+
runs([wrapped.argv]);
|
|
17052
|
+
dir = outer;
|
|
17053
|
+
return;
|
|
17054
|
+
}
|
|
16070
17055
|
if (CD.has(name)) {
|
|
16071
17056
|
const target = cdTarget(words);
|
|
16072
17057
|
dir = target === null ? os4.homedir() : joinDir(dir, target);
|
|
@@ -16167,14 +17152,14 @@ function workitCheckTail(words) {
|
|
|
16167
17152
|
return null;
|
|
16168
17153
|
} else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
|
|
16169
17154
|
return null;
|
|
16170
|
-
const args = words.slice(at + 1);
|
|
17155
|
+
const { rest: args, cwd } = skipWorkitGlobals(words.slice(at + 1));
|
|
16171
17156
|
if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
|
|
16172
17157
|
return null;
|
|
16173
17158
|
const split = args.indexOf("--");
|
|
16174
17159
|
const tail = split < 0 ? [] : args.slice(split + 1);
|
|
16175
17160
|
if (!tail.length)
|
|
16176
17161
|
return null;
|
|
16177
|
-
return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
|
|
17162
|
+
return args.slice(0, split).includes("--shell") ? { script: tail.join(" "), cwd } : { argv: tail, cwd };
|
|
16178
17163
|
}
|
|
16179
17164
|
function simpleCommand(command, dialect = "posix", depth = 0) {
|
|
16180
17165
|
let segments;
|
|
@@ -16235,7 +17220,21 @@ function skipWorkitGlobals(args) {
|
|
|
16235
17220
|
} else if (args[at] !== "--json")
|
|
16236
17221
|
break;
|
|
16237
17222
|
}
|
|
16238
|
-
|
|
17223
|
+
const rest = [];
|
|
17224
|
+
for (;at < args.length; at++) {
|
|
17225
|
+
const arg = args[at];
|
|
17226
|
+
if (arg === "--") {
|
|
17227
|
+
rest.push(...args.slice(at));
|
|
17228
|
+
break;
|
|
17229
|
+
}
|
|
17230
|
+
if (arg.startsWith("--cwd="))
|
|
17231
|
+
cwd = arg.slice("--cwd=".length);
|
|
17232
|
+
else if (arg === "--cwd" && args[at + 1] !== undefined)
|
|
17233
|
+
cwd = args[++at];
|
|
17234
|
+
else
|
|
17235
|
+
rest.push(arg);
|
|
17236
|
+
}
|
|
17237
|
+
return { rest, cwd };
|
|
16239
17238
|
}
|
|
16240
17239
|
var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
16241
17240
|
var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
|
|
@@ -16536,17 +17535,17 @@ var headSha2 = (repo) => {
|
|
|
16536
17535
|
if (!git || git.reftable)
|
|
16537
17536
|
return null;
|
|
16538
17537
|
try {
|
|
16539
|
-
const head = fs16.readFileSync(
|
|
17538
|
+
const head = fs16.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
16540
17539
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
16541
17540
|
return head;
|
|
16542
17541
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
16543
17542
|
if (!ref)
|
|
16544
17543
|
return null;
|
|
16545
|
-
const common =
|
|
17544
|
+
const common = path20.dirname(repo.location.dir);
|
|
16546
17545
|
try {
|
|
16547
|
-
return fs16.readFileSync(
|
|
17546
|
+
return fs16.readFileSync(path20.join(common, ref), "utf8").trim() || null;
|
|
16548
17547
|
} catch {}
|
|
16549
|
-
const packed = fs16.readFileSync(
|
|
17548
|
+
const packed = fs16.readFileSync(path20.join(common, "packed-refs"), "utf8");
|
|
16550
17549
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
16551
17550
|
} catch {
|
|
16552
17551
|
return null;
|
|
@@ -16634,7 +17633,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
16634
17633
|
const sub = invocation.args[0];
|
|
16635
17634
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
16636
17635
|
return [invocation];
|
|
16637
|
-
const dir =
|
|
17636
|
+
const dir = path20.resolve(root, invocation.dir ?? ".");
|
|
16638
17637
|
if (!fs16.existsSync(dir))
|
|
16639
17638
|
return [invocation];
|
|
16640
17639
|
const config = gitConfig(dir, invocation, cache);
|
|
@@ -16657,11 +17656,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
16657
17656
|
var normalizedUrl = (url, dir) => {
|
|
16658
17657
|
const trimmed = url.trim();
|
|
16659
17658
|
if (/^file:\/\//i.test(trimmed))
|
|
16660
|
-
return
|
|
17659
|
+
return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
16661
17660
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
16662
17661
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
16663
17662
|
if (!scheme && !scp)
|
|
16664
|
-
return
|
|
17663
|
+
return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
16665
17664
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
16666
17665
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
16667
17666
|
};
|
|
@@ -16885,7 +17884,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
|
|
|
16885
17884
|
const out = [];
|
|
16886
17885
|
const repos = new Map;
|
|
16887
17886
|
const repoFor = (relative) => {
|
|
16888
|
-
const dir =
|
|
17887
|
+
const dir = path20.resolve(cwd, relative ?? ".");
|
|
16889
17888
|
if (!repos.has(dir)) {
|
|
16890
17889
|
const repo = repoAt(dir);
|
|
16891
17890
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -16970,7 +17969,7 @@ var analysisDenial = (error) => ({
|
|
|
16970
17969
|
unblock: SPLIT_IT
|
|
16971
17970
|
});
|
|
16972
17971
|
var MARKER_TTL_SECONDS = 3600;
|
|
16973
|
-
var markerFile = (repo, session, key) =>
|
|
17972
|
+
var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
16974
17973
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
16975
17974
|
function noteRawCommit(input, command, key) {
|
|
16976
17975
|
try {
|
|
@@ -16981,7 +17980,7 @@ function noteRawCommit(input, command, key) {
|
|
|
16981
17980
|
if (action.kind !== "commit")
|
|
16982
17981
|
continue;
|
|
16983
17982
|
const file = markerFile(repo, input.session.id, key);
|
|
16984
|
-
fs16.mkdirSync(
|
|
17983
|
+
fs16.mkdirSync(path20.dirname(file), { recursive: true });
|
|
16985
17984
|
fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
16986
17985
|
`);
|
|
16987
17986
|
}
|
|
@@ -17200,8 +18199,8 @@ var judgedDir = (root, dir) => {
|
|
|
17200
18199
|
return null;
|
|
17201
18200
|
if (dir === null)
|
|
17202
18201
|
return root;
|
|
17203
|
-
const resolved =
|
|
17204
|
-
return
|
|
18202
|
+
const resolved = path21.resolve(root, dir);
|
|
18203
|
+
return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
17205
18204
|
};
|
|
17206
18205
|
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
17207
18206
|
let judged = false;
|
|
@@ -17436,7 +18435,7 @@ var judgeInputSummary = (raw) => {
|
|
|
17436
18435
|
|
|
17437
18436
|
// packages/workit-core/src/autonomy.ts
|
|
17438
18437
|
import os5 from "node:os";
|
|
17439
|
-
import
|
|
18438
|
+
import path22 from "node:path";
|
|
17440
18439
|
|
|
17441
18440
|
// packages/workit-core/src/core/safe-write.ts
|
|
17442
18441
|
import {
|
|
@@ -17470,10 +18469,10 @@ var accountHome = () => {
|
|
|
17470
18469
|
}
|
|
17471
18470
|
};
|
|
17472
18471
|
var grantsHome = { resolve: accountHome };
|
|
17473
|
-
var grantsDir = () =>
|
|
18472
|
+
var grantsDir = () => path22.join(grantsHome.resolve(), ".config", "workit");
|
|
17474
18473
|
var grantsOverride = () => {
|
|
17475
|
-
const configured =
|
|
17476
|
-
return configured ===
|
|
18474
|
+
const configured = path22.resolve(resolveConfigDir());
|
|
18475
|
+
return configured === path22.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 ${path22.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
17477
18476
|
};
|
|
17478
18477
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
17479
18478
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -17526,8 +18525,16 @@ function endpointView(entry, context) {
|
|
|
17526
18525
|
...issue ? { endpointIssue: issue } : {}
|
|
17527
18526
|
};
|
|
17528
18527
|
}
|
|
17529
|
-
var
|
|
17530
|
-
|
|
18528
|
+
var VERIFICATION_MODES = [
|
|
18529
|
+
"proportional",
|
|
18530
|
+
"self",
|
|
18531
|
+
"independent"
|
|
18532
|
+
];
|
|
18533
|
+
var DEFAULT_VERIFICATION = "proportional";
|
|
18534
|
+
var verificationOf = (entry) => {
|
|
18535
|
+
const value = entry?.verification;
|
|
18536
|
+
return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
|
|
18537
|
+
};
|
|
17531
18538
|
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
17532
18539
|
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
17533
18540
|
var fromAutoApprove = (value) => {
|
|
@@ -19275,8 +20282,8 @@ class WorkitCore {
|
|
|
19275
20282
|
}
|
|
19276
20283
|
// packages/workit-core/src/core/repo-context.ts
|
|
19277
20284
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
19278
|
-
import { existsSync as
|
|
19279
|
-
import
|
|
20285
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
20286
|
+
import path23 from "node:path";
|
|
19280
20287
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
19281
20288
|
try {
|
|
19282
20289
|
gitRevisionParts(value);
|
|
@@ -19410,7 +20417,7 @@ var findPrTemplate = (cwd) => {
|
|
|
19410
20417
|
]) {
|
|
19411
20418
|
const parts = rel.split("/");
|
|
19412
20419
|
const base = parts[parts.length - 1];
|
|
19413
|
-
const dir = parts.length > 1 ?
|
|
20420
|
+
const dir = parts.length > 1 ? path23.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
19414
20421
|
const actual = probeCaseInsensitive(dir, base);
|
|
19415
20422
|
if (actual)
|
|
19416
20423
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -19441,11 +20448,11 @@ var diffStatForRange = (cwd, range) => {
|
|
|
19441
20448
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
19442
20449
|
};
|
|
19443
20450
|
var packageScripts = (cwd, keys) => {
|
|
19444
|
-
const pkgPath =
|
|
19445
|
-
if (!
|
|
20451
|
+
const pkgPath = path23.join(cwd, "package.json");
|
|
20452
|
+
if (!existsSync8(pkgPath))
|
|
19446
20453
|
return [];
|
|
19447
20454
|
try {
|
|
19448
|
-
const pkg = JSON.parse(
|
|
20455
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
19449
20456
|
return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
|
|
19450
20457
|
} catch {
|
|
19451
20458
|
return [];
|
|
@@ -19467,9 +20474,9 @@ var documentationFiles = (cwd) => {
|
|
|
19467
20474
|
if (entry.isDirectory()) {
|
|
19468
20475
|
if (excluded.has(entry.name))
|
|
19469
20476
|
continue;
|
|
19470
|
-
walk(
|
|
20477
|
+
walk(path23.join(dir, entry.name), depth + 1);
|
|
19471
20478
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
19472
|
-
matches.push("./" +
|
|
20479
|
+
matches.push("./" + path23.relative(cwd, path23.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
19473
20480
|
}
|
|
19474
20481
|
}
|
|
19475
20482
|
};
|
|
@@ -19477,15 +20484,15 @@ var documentationFiles = (cwd) => {
|
|
|
19477
20484
|
return matches.toSorted().slice(0, 200);
|
|
19478
20485
|
};
|
|
19479
20486
|
var readTrimmed2 = (file, maxLines) => {
|
|
19480
|
-
if (!
|
|
20487
|
+
if (!existsSync8(file))
|
|
19481
20488
|
return "";
|
|
19482
|
-
const lines =
|
|
20489
|
+
const lines = readFileSync6(file, "utf8").split(`
|
|
19483
20490
|
`);
|
|
19484
20491
|
return lines.slice(0, maxLines).join(`
|
|
19485
20492
|
`);
|
|
19486
20493
|
};
|
|
19487
20494
|
function prReadyContext(root, range) {
|
|
19488
|
-
const cwd =
|
|
20495
|
+
const cwd = path23.resolve(repoRoot(root));
|
|
19489
20496
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
19490
20497
|
return invalidRange(cwd);
|
|
19491
20498
|
let stdout = "";
|
|
@@ -19543,7 +20550,7 @@ ${status.stderr}` : "");
|
|
|
19543
20550
|
stdout += `template_path: ${template}
|
|
19544
20551
|
|
|
19545
20552
|
`;
|
|
19546
|
-
stdout += readTrimmed2(
|
|
20553
|
+
stdout += readTrimmed2(path23.join(cwd, template), 220);
|
|
19547
20554
|
} else {
|
|
19548
20555
|
stdout += `template_path: none
|
|
19549
20556
|
|
|
@@ -19553,7 +20560,7 @@ ${status.stderr}` : "");
|
|
|
19553
20560
|
stdout += `
|
|
19554
20561
|
`;
|
|
19555
20562
|
stdout += printSection("Recent Validation Signals");
|
|
19556
|
-
if (
|
|
20563
|
+
if (existsSync8(path23.join(cwd, "package.json"))) {
|
|
19557
20564
|
stdout += `package.json detected. Common scripts:
|
|
19558
20565
|
`;
|
|
19559
20566
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -19562,7 +20569,7 @@ ${status.stderr}` : "");
|
|
|
19562
20569
|
`) + `
|
|
19563
20570
|
`;
|
|
19564
20571
|
}
|
|
19565
|
-
if (
|
|
20572
|
+
if (existsSync8(path23.join(cwd, "Cargo.toml")) || existsSync8(path23.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
19566
20573
|
stdout += `Rust project detected.
|
|
19567
20574
|
`;
|
|
19568
20575
|
}
|
|
@@ -19595,7 +20602,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
19595
20602
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
19596
20603
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
19597
20604
|
function changelogContext(root, range) {
|
|
19598
|
-
const cwd =
|
|
20605
|
+
const cwd = path23.resolve(repoRoot(root));
|
|
19599
20606
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
19600
20607
|
return invalidRange(cwd);
|
|
19601
20608
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -19613,8 +20620,8 @@ function changelogContext(root, range) {
|
|
|
19613
20620
|
stdout += CHANGELOG_RULES + `
|
|
19614
20621
|
`;
|
|
19615
20622
|
stdout += printSection("Existing CHANGELOG.md");
|
|
19616
|
-
const changelogPath =
|
|
19617
|
-
if (
|
|
20623
|
+
const changelogPath = path23.join(cwd, "CHANGELOG.md");
|
|
20624
|
+
if (existsSync8(changelogPath)) {
|
|
19618
20625
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
19619
20626
|
`;
|
|
19620
20627
|
} else {
|
|
@@ -19633,7 +20640,7 @@ function changelogContext(root, range) {
|
|
|
19633
20640
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
19634
20641
|
}
|
|
19635
20642
|
function docsRefreshContext(root, range) {
|
|
19636
|
-
const cwd =
|
|
20643
|
+
const cwd = path23.resolve(repoRoot(root));
|
|
19637
20644
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
19638
20645
|
return invalidRange(cwd);
|
|
19639
20646
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -19655,8 +20662,8 @@ function docsRefreshContext(root, range) {
|
|
|
19655
20662
|
`) + `
|
|
19656
20663
|
`;
|
|
19657
20664
|
stdout += printSection("README Preview");
|
|
19658
|
-
const readme =
|
|
19659
|
-
if (
|
|
20665
|
+
const readme = path23.join(cwd, "README.md");
|
|
20666
|
+
if (existsSync8(readme)) {
|
|
19660
20667
|
stdout += readTrimmed2(readme, 220) + `
|
|
19661
20668
|
`;
|
|
19662
20669
|
} else {
|
|
@@ -19664,10 +20671,10 @@ function docsRefreshContext(root, range) {
|
|
|
19664
20671
|
`;
|
|
19665
20672
|
}
|
|
19666
20673
|
stdout += printSection("Package Scripts");
|
|
19667
|
-
const pkgPath =
|
|
19668
|
-
if (
|
|
20674
|
+
const pkgPath = path23.join(cwd, "package.json");
|
|
20675
|
+
if (existsSync8(pkgPath)) {
|
|
19669
20676
|
try {
|
|
19670
|
-
const pkg = JSON.parse(
|
|
20677
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
19671
20678
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
19672
20679
|
`;
|
|
19673
20680
|
} catch {}
|
|
@@ -19678,7 +20685,7 @@ function docsRefreshContext(root, range) {
|
|
|
19678
20685
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
19679
20686
|
}
|
|
19680
20687
|
function releaseNotesContext(root, rangeOrTag) {
|
|
19681
|
-
const cwd =
|
|
20688
|
+
const cwd = path23.resolve(repoRoot(root));
|
|
19682
20689
|
if (!rangeOrTag) {
|
|
19683
20690
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
19684
20691
|
`, exitCode: 1, cwd };
|
|
@@ -19716,7 +20723,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
19716
20723
|
`;
|
|
19717
20724
|
stdout += printSection("Existing Release Files");
|
|
19718
20725
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
19719
|
-
if (
|
|
20726
|
+
if (existsSync8(path23.join(cwd, rel)))
|
|
19720
20727
|
stdout += rel + `
|
|
19721
20728
|
`;
|
|
19722
20729
|
}
|
|
@@ -19748,7 +20755,7 @@ import { spawnSync as spawnSync15 } from "node:child_process";
|
|
|
19748
20755
|
|
|
19749
20756
|
// packages/workit-core/src/core/pr-create.ts
|
|
19750
20757
|
import fs17 from "node:fs";
|
|
19751
|
-
import
|
|
20758
|
+
import path24 from "node:path";
|
|
19752
20759
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
19753
20760
|
import { pathToFileURL } from "node:url";
|
|
19754
20761
|
// packages/workit-core/src/forge/pr-body.ts
|
|
@@ -19760,11 +20767,11 @@ function parseGhIssue(value) {
|
|
|
19760
20767
|
// packages/workit-core/src/core/pr-create.ts
|
|
19761
20768
|
function whichOnPath(tool) {
|
|
19762
20769
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
19763
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
20770
|
+
for (const dir of (process.env.PATH ?? "").split(path24.delimiter)) {
|
|
19764
20771
|
if (!dir)
|
|
19765
20772
|
continue;
|
|
19766
20773
|
for (const name of names) {
|
|
19767
|
-
const candidate =
|
|
20774
|
+
const candidate = path24.join(dir, name);
|
|
19768
20775
|
try {
|
|
19769
20776
|
fs17.accessSync(candidate, fs17.constants.X_OK);
|
|
19770
20777
|
return candidate;
|
|
@@ -19786,7 +20793,7 @@ var pushRemote = (cwd) => {
|
|
|
19786
20793
|
};
|
|
19787
20794
|
var safePushUrl = (remote) => {
|
|
19788
20795
|
const raw = remote.trim();
|
|
19789
|
-
if (
|
|
20796
|
+
if (path24.isAbsolute(raw))
|
|
19790
20797
|
return pathToFileURL(raw).href;
|
|
19791
20798
|
if (!raw.includes("://")) {
|
|
19792
20799
|
const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
|
|
@@ -19918,7 +20925,7 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
19918
20925
|
|
|
19919
20926
|
// packages/workit-core/src/core/youtrack.ts
|
|
19920
20927
|
import fs18 from "node:fs";
|
|
19921
|
-
import
|
|
20928
|
+
import path25 from "node:path";
|
|
19922
20929
|
|
|
19923
20930
|
// packages/workit-core/src/core/templates.ts
|
|
19924
20931
|
var repoRoot2 = assetRoot();
|
|
@@ -19931,8 +20938,8 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
19931
20938
|
// packages/workit-core/src/core/youtrack.ts
|
|
19932
20939
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
19933
20940
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
19934
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
19935
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
20941
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path25.join(configDir(), "youtrack.json");
|
|
20942
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path25.join(resolveConfigDir(), "youtrack.json");
|
|
19936
20943
|
var youTrackTokenModeOk = (p) => {
|
|
19937
20944
|
if (process.platform === "win32")
|
|
19938
20945
|
return true;
|
|
@@ -20000,7 +21007,7 @@ var youTrackToken = () => {
|
|
|
20000
21007
|
if ("error" in loaded)
|
|
20001
21008
|
return loaded;
|
|
20002
21009
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
20003
|
-
const tokenPath = tokenFile ?
|
|
21010
|
+
const tokenPath = tokenFile ? path25.isAbsolute(tokenFile) ? path25.resolve(tokenFile) : path25.resolve(process.cwd(), tokenFile) : "";
|
|
20004
21011
|
if (!tokenPath || !fs18.existsSync(tokenPath))
|
|
20005
21012
|
return { error: "missing youtrack.token" };
|
|
20006
21013
|
if (!youTrackTokenModeOk(tokenPath))
|
|
@@ -20051,7 +21058,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20051
21058
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
20052
21059
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
20053
21060
|
const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
|
|
20054
|
-
const tokenFile = String(config.tokenFile ??
|
|
21061
|
+
const tokenFile = String(config.tokenFile ?? path25.join(path25.dirname(loaded.path), "youtrack.token"));
|
|
20055
21062
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
20056
21063
|
const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
|
|
20057
21064
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -20060,7 +21067,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20060
21067
|
tokenName: name,
|
|
20061
21068
|
tokenDescription: desc,
|
|
20062
21069
|
scopes,
|
|
20063
|
-
tokenFile:
|
|
21070
|
+
tokenFile: path25.resolve(tokenFile),
|
|
20064
21071
|
createUrl,
|
|
20065
21072
|
docsUrl,
|
|
20066
21073
|
prefillSupported: false,
|
|
@@ -20400,15 +21407,15 @@ function resolveGitRevision(root, value) {
|
|
|
20400
21407
|
}
|
|
20401
21408
|
function resolveInside(root, candidate) {
|
|
20402
21409
|
const base = realpathSync5(root);
|
|
20403
|
-
const target =
|
|
20404
|
-
if (target !== base && !target.startsWith(base +
|
|
21410
|
+
const target = path26.resolve(base, candidate);
|
|
21411
|
+
if (target !== base && !target.startsWith(base + path26.sep)) {
|
|
20405
21412
|
throw new Error("path must stay inside repository root");
|
|
20406
21413
|
}
|
|
20407
21414
|
let ancestor = target;
|
|
20408
|
-
while (!
|
|
20409
|
-
ancestor =
|
|
21415
|
+
while (!existsSync9(ancestor))
|
|
21416
|
+
ancestor = path26.dirname(ancestor);
|
|
20410
21417
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
20411
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
21418
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path26.sep)) {
|
|
20412
21419
|
throw new Error("path must stay inside repository root");
|
|
20413
21420
|
}
|
|
20414
21421
|
return target;
|
|
@@ -20452,7 +21459,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
20452
21459
|
};
|
|
20453
21460
|
});
|
|
20454
21461
|
// packages/workit-core/src/hooks/context.ts
|
|
20455
|
-
import
|
|
21462
|
+
import path27 from "node:path";
|
|
20456
21463
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
20457
21464
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
20458
21465
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -20537,7 +21544,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
20537
21544
|
return null;
|
|
20538
21545
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
20539
21546
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
20540
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
21547
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path27.resolve(store.root, ref.path)] : []));
|
|
20541
21548
|
const observedFiles = filesKey(files);
|
|
20542
21549
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
20543
21550
|
if (!compact.ok || !compact.data)
|
|
@@ -20567,10 +21574,61 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
20567
21574
|
}
|
|
20568
21575
|
return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
|
|
20569
21576
|
};
|
|
21577
|
+
// packages/workit-core/src/hooks/delivery-verbs.ts
|
|
21578
|
+
var WORKIT_ALLOWED_VERBS = [
|
|
21579
|
+
"git",
|
|
21580
|
+
"pr",
|
|
21581
|
+
"ci",
|
|
21582
|
+
"stack",
|
|
21583
|
+
"release",
|
|
21584
|
+
"task",
|
|
21585
|
+
"ledger",
|
|
21586
|
+
"evidence",
|
|
21587
|
+
"finding",
|
|
21588
|
+
"decision",
|
|
21589
|
+
"worker",
|
|
21590
|
+
"verify-delivery",
|
|
21591
|
+
"policy",
|
|
21592
|
+
"doctor",
|
|
21593
|
+
"help"
|
|
21594
|
+
];
|
|
21595
|
+
var WORKIT_BYPASS_FLAGS = ["--unverified", "--overwrite-unintegrated"];
|
|
21596
|
+
var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
|
|
21597
|
+
var isBypass = (word) => WORKIT_BYPASS_FLAGS.some((flag) => word === flag || word.startsWith(`${flag}=`));
|
|
21598
|
+
var isAllowedWorkitCall = (words, expands = []) => {
|
|
21599
|
+
if (words[0] !== "workit")
|
|
21600
|
+
return false;
|
|
21601
|
+
let at = 1;
|
|
21602
|
+
for (;; ) {
|
|
21603
|
+
const word = words[at];
|
|
21604
|
+
if (word === "--json")
|
|
21605
|
+
at += 1;
|
|
21606
|
+
else if (word === "--cwd")
|
|
21607
|
+
at += 2;
|
|
21608
|
+
else if (word?.startsWith("--cwd="))
|
|
21609
|
+
at += 1;
|
|
21610
|
+
else
|
|
21611
|
+
break;
|
|
21612
|
+
}
|
|
21613
|
+
const verb = words[at];
|
|
21614
|
+
if (verb === undefined)
|
|
21615
|
+
return false;
|
|
21616
|
+
if (verb === "--version")
|
|
21617
|
+
return at === words.length - 1;
|
|
21618
|
+
if (!ALLOWED.has(verb))
|
|
21619
|
+
return false;
|
|
21620
|
+
const end = words.indexOf("--");
|
|
21621
|
+
const limit = end < 0 ? words.length : end;
|
|
21622
|
+
return !words.slice(0, limit).some(isBypass) && !expands.some((index) => index < limit);
|
|
21623
|
+
};
|
|
21624
|
+
var isAllowedWorkitCommand = (command) => {
|
|
21625
|
+
const segments = segmentsOf(command);
|
|
21626
|
+
return segments.length > 0 && segments.every((s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length && isAllowedWorkitCall(s.words, s.expands));
|
|
21627
|
+
};
|
|
20570
21628
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
20571
21629
|
import fs19 from "node:fs";
|
|
20572
21630
|
import os6 from "node:os";
|
|
20573
|
-
import
|
|
21631
|
+
import path28 from "node:path";
|
|
20574
21632
|
var EMPTY2 = () => ({ deny: [], allow: [] });
|
|
20575
21633
|
var readText2 = (file) => {
|
|
20576
21634
|
try {
|
|
@@ -20611,19 +21669,19 @@ function parseJsonc(text) {
|
|
|
20611
21669
|
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
20612
21670
|
}
|
|
20613
21671
|
function gitTop(cwd) {
|
|
20614
|
-
for (let dir =
|
|
20615
|
-
if (fs19.existsSync(
|
|
21672
|
+
for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
|
|
21673
|
+
if (fs19.existsSync(path28.join(dir, ".git")))
|
|
20616
21674
|
return dir;
|
|
20617
|
-
if (
|
|
21675
|
+
if (path28.dirname(dir) === dir)
|
|
20618
21676
|
return null;
|
|
20619
21677
|
}
|
|
20620
21678
|
}
|
|
20621
21679
|
var projectDirs = (cwd) => {
|
|
20622
21680
|
const top = gitTop(cwd);
|
|
20623
21681
|
const dirs = [];
|
|
20624
|
-
for (let dir =
|
|
21682
|
+
for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
|
|
20625
21683
|
dirs.push(dir);
|
|
20626
|
-
if (!top || dir === top ||
|
|
21684
|
+
if (!top || dir === top || path28.dirname(dir) === dir)
|
|
20627
21685
|
return dirs;
|
|
20628
21686
|
}
|
|
20629
21687
|
};
|
|
@@ -20635,12 +21693,12 @@ var CLAUDE_MANAGED = {
|
|
|
20635
21693
|
};
|
|
20636
21694
|
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
20637
21695
|
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
20638
|
-
const user = env.CLAUDE_CONFIG_DIR ||
|
|
21696
|
+
const user = env.CLAUDE_CONFIG_DIR || path28.join(homeOf(env), ".claude");
|
|
20639
21697
|
return [
|
|
20640
21698
|
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
20641
|
-
|
|
20642
|
-
|
|
20643
|
-
|
|
21699
|
+
path28.join(user, "settings.json"),
|
|
21700
|
+
path28.join(project, ".claude", "settings.json"),
|
|
21701
|
+
path28.join(project, ".claude", "settings.local.json")
|
|
20644
21702
|
];
|
|
20645
21703
|
}
|
|
20646
21704
|
var bashPattern = (entry) => {
|
|
@@ -20730,10 +21788,10 @@ var opencodeConfig = (value, rules) => {
|
|
|
20730
21788
|
function opencodeRules(cwd, env = process.env) {
|
|
20731
21789
|
const rules = EMPTY2();
|
|
20732
21790
|
try {
|
|
20733
|
-
const config = env.XDG_CONFIG_HOME ||
|
|
21791
|
+
const config = env.XDG_CONFIG_HOME || path28.join(homeOf(env), ".config");
|
|
20734
21792
|
const files = [
|
|
20735
|
-
...OPENCODE_NAMES.map((name) =>
|
|
20736
|
-
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [
|
|
21793
|
+
...OPENCODE_NAMES.map((name) => path28.join(config, "opencode", name)),
|
|
21794
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path28.join(dir, name), path28.join(dir, ".opencode", name)])),
|
|
20737
21795
|
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
20738
21796
|
];
|
|
20739
21797
|
for (const file of files) {
|
|
@@ -20777,9 +21835,9 @@ var codexRule = (body) => {
|
|
|
20777
21835
|
function codexRules(cwd, env = process.env) {
|
|
20778
21836
|
const rules = EMPTY2();
|
|
20779
21837
|
try {
|
|
20780
|
-
const home = env.CODEX_HOME ||
|
|
21838
|
+
const home = env.CODEX_HOME || path28.join(homeOf(env), ".codex");
|
|
20781
21839
|
const top = gitTop(cwd) ?? cwd;
|
|
20782
|
-
for (const dir of [
|
|
21840
|
+
for (const dir of [path28.join(home, "rules"), path28.join(top, ".codex", "rules")]) {
|
|
20783
21841
|
let files;
|
|
20784
21842
|
try {
|
|
20785
21843
|
files = fs19.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
@@ -20789,7 +21847,7 @@ function codexRules(cwd, env = process.env) {
|
|
|
20789
21847
|
return "unreadable";
|
|
20790
21848
|
}
|
|
20791
21849
|
for (const name of files) {
|
|
20792
|
-
const text = readText2(
|
|
21850
|
+
const text = readText2(path28.join(dir, name)) ?? "";
|
|
20793
21851
|
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
20794
21852
|
const rule = codexRule(part);
|
|
20795
21853
|
if (!rule)
|
|
@@ -20808,8 +21866,8 @@ function cursorRules(cwd, env = process.env) {
|
|
|
20808
21866
|
try {
|
|
20809
21867
|
const top = gitTop(cwd) ?? cwd;
|
|
20810
21868
|
for (const file of [
|
|
20811
|
-
|
|
20812
|
-
|
|
21869
|
+
path28.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
21870
|
+
path28.join(top, ".cursor", "cli.json")
|
|
20813
21871
|
]) {
|
|
20814
21872
|
const text = readText2(file);
|
|
20815
21873
|
if (text === null)
|
|
@@ -20885,21 +21943,21 @@ function wrapAllowed(read, words, command, options = {}) {
|
|
|
20885
21943
|
// packages/workit-core/src/core/heavy.ts
|
|
20886
21944
|
import fs20 from "node:fs";
|
|
20887
21945
|
import os7 from "node:os";
|
|
20888
|
-
import
|
|
21946
|
+
import path29 from "node:path";
|
|
20889
21947
|
function stateDir(env, platform = process.platform) {
|
|
20890
21948
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
20891
21949
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
20892
21950
|
if (env.XDG_STATE_HOME)
|
|
20893
|
-
return
|
|
21951
|
+
return path29.join(env.XDG_STATE_HOME, "workit");
|
|
20894
21952
|
const home = env.HOME || os7.homedir();
|
|
20895
21953
|
if (platform === "darwin")
|
|
20896
|
-
return
|
|
21954
|
+
return path29.join(home, "Library", "Application Support", "workit");
|
|
20897
21955
|
if (platform === "win32")
|
|
20898
|
-
return
|
|
20899
|
-
return
|
|
21956
|
+
return path29.join(env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local"), "workit");
|
|
21957
|
+
return path29.join(home, ".local", "state", "workit");
|
|
20900
21958
|
}
|
|
20901
21959
|
var HEAVY_COMMANDS_FILE = "heavy-commands.json";
|
|
20902
|
-
var heavyCommandsFile = (env) =>
|
|
21960
|
+
var heavyCommandsFile = (env) => path29.join(stateDir(env), HEAVY_COMMANDS_FILE);
|
|
20903
21961
|
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;
|
|
20904
21962
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
20905
21963
|
var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
@@ -21174,7 +22232,7 @@ function machinePatterns(env) {
|
|
|
21174
22232
|
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
21175
22233
|
}
|
|
21176
22234
|
function repoPatterns(cwd) {
|
|
21177
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
22235
|
+
return repoHeavyCommands(gitTop(cwd) ?? path29.resolve(cwd));
|
|
21178
22236
|
}
|
|
21179
22237
|
function heavyLines(lines, options) {
|
|
21180
22238
|
const found = [];
|
|
@@ -21252,12 +22310,12 @@ function heavyPlan(command, options) {
|
|
|
21252
22310
|
}
|
|
21253
22311
|
function workitOnPath2(env = process.env) {
|
|
21254
22312
|
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
21255
|
-
for (const dir of (env.PATH ?? "").split(
|
|
22313
|
+
for (const dir of (env.PATH ?? "").split(path29.delimiter))
|
|
21256
22314
|
for (const name of names) {
|
|
21257
22315
|
if (!dir)
|
|
21258
22316
|
continue;
|
|
21259
22317
|
try {
|
|
21260
|
-
fs20.accessSync(
|
|
22318
|
+
fs20.accessSync(path29.join(dir, name), fs20.constants.X_OK);
|
|
21261
22319
|
return true;
|
|
21262
22320
|
} catch {}
|
|
21263
22321
|
}
|
|
@@ -21276,20 +22334,20 @@ function leaseRewrite(command) {
|
|
|
21276
22334
|
// packages/workit-core/src/core/heavy-words.ts
|
|
21277
22335
|
import fs21 from "node:fs";
|
|
21278
22336
|
import os8 from "node:os";
|
|
21279
|
-
import
|
|
22337
|
+
import path30 from "node:path";
|
|
21280
22338
|
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(" "));
|
|
21281
22339
|
var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
|
|
21282
22340
|
var stateDir2 = (env) => {
|
|
21283
22341
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
21284
22342
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
21285
22343
|
if (env.XDG_STATE_HOME)
|
|
21286
|
-
return
|
|
22344
|
+
return path30.join(env.XDG_STATE_HOME, "workit");
|
|
21287
22345
|
const home = env.HOME || os8.homedir();
|
|
21288
22346
|
if (process.platform === "darwin")
|
|
21289
|
-
return
|
|
22347
|
+
return path30.join(home, "Library", "Application Support", "workit");
|
|
21290
22348
|
if (process.platform === "win32")
|
|
21291
|
-
return
|
|
21292
|
-
return
|
|
22349
|
+
return path30.join(env.LOCALAPPDATA ?? path30.join(home, "AppData", "Local"), "workit");
|
|
22350
|
+
return path30.join(home, ".local", "state", "workit");
|
|
21293
22351
|
};
|
|
21294
22352
|
var readJson2 = (file) => {
|
|
21295
22353
|
try {
|
|
@@ -21300,15 +22358,15 @@ var readJson2 = (file) => {
|
|
|
21300
22358
|
};
|
|
21301
22359
|
var patternWords = (cwd, env) => {
|
|
21302
22360
|
const patterns = [];
|
|
21303
|
-
const machine = readJson2(
|
|
22361
|
+
const machine = readJson2(path30.join(stateDir2(env), "heavy-commands.json"));
|
|
21304
22362
|
if (Array.isArray(machine?.patterns))
|
|
21305
22363
|
patterns.push(...machine.patterns);
|
|
21306
|
-
for (let dir =
|
|
21307
|
-
if (fs21.existsSync(
|
|
22364
|
+
for (let dir = path30.resolve(cwd);; dir = path30.dirname(dir)) {
|
|
22365
|
+
if (fs21.existsSync(path30.join(dir, ".git"))) {
|
|
21308
22366
|
patterns.push(...repoHeavyCommands(dir));
|
|
21309
22367
|
break;
|
|
21310
22368
|
}
|
|
21311
|
-
if (
|
|
22369
|
+
if (path30.dirname(dir) === dir)
|
|
21312
22370
|
break;
|
|
21313
22371
|
}
|
|
21314
22372
|
const words = new Set;
|
|
@@ -21367,16 +22425,16 @@ function heavyGate(cwd, command, options) {
|
|
|
21367
22425
|
}
|
|
21368
22426
|
}
|
|
21369
22427
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
21370
|
-
import { existsSync as
|
|
22428
|
+
import { existsSync as existsSync10, realpathSync as realpathSync6 } from "node:fs";
|
|
21371
22429
|
import os10 from "node:os";
|
|
21372
|
-
import
|
|
22430
|
+
import path32 from "node:path";
|
|
21373
22431
|
|
|
21374
22432
|
// packages/workit-core/src/core/live-session.ts
|
|
21375
22433
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
21376
22434
|
import { createHash as createHash15 } from "node:crypto";
|
|
21377
22435
|
import fs22 from "node:fs";
|
|
21378
22436
|
import os9 from "node:os";
|
|
21379
|
-
import
|
|
22437
|
+
import path31 from "node:path";
|
|
21380
22438
|
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
21381
22439
|
var HEARTBEAT_EVERY_MS = 30000;
|
|
21382
22440
|
var liveProbe = {
|
|
@@ -21450,15 +22508,15 @@ var liveOwner = (input, transport, env = process.env) => {
|
|
|
21450
22508
|
};
|
|
21451
22509
|
};
|
|
21452
22510
|
var checkoutOf = (target) => {
|
|
21453
|
-
let dir =
|
|
21454
|
-
while (!fs22.existsSync(dir) &&
|
|
21455
|
-
dir =
|
|
22511
|
+
let dir = path31.resolve(target);
|
|
22512
|
+
while (!fs22.existsSync(dir) && path31.dirname(dir) !== dir)
|
|
22513
|
+
dir = path31.dirname(dir);
|
|
21456
22514
|
if (fs22.existsSync(dir) && !fs22.statSync(dir).isDirectory())
|
|
21457
|
-
dir =
|
|
21458
|
-
const parts = dir.split(
|
|
22515
|
+
dir = path31.dirname(dir);
|
|
22516
|
+
const parts = dir.split(path31.sep);
|
|
21459
22517
|
const dotGit = parts.lastIndexOf(".git");
|
|
21460
22518
|
if (dotGit > 0)
|
|
21461
|
-
dir = parts.slice(0, dotGit).join(
|
|
22519
|
+
dir = parts.slice(0, dotGit).join(path31.sep) || path31.sep;
|
|
21462
22520
|
const location = resolveStore(dir);
|
|
21463
22521
|
if (location instanceof Error || !location.shared || !location.top)
|
|
21464
22522
|
return null;
|
|
@@ -21468,9 +22526,9 @@ var checkoutOf = (target) => {
|
|
|
21468
22526
|
} catch {}
|
|
21469
22527
|
return top;
|
|
21470
22528
|
};
|
|
21471
|
-
var markerFile2 = (top) =>
|
|
22529
|
+
var markerFile2 = (top) => path31.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
21472
22530
|
var checkoutKey = (top, platform = process.platform) => {
|
|
21473
|
-
let key =
|
|
22531
|
+
let key = path31.resolve(top);
|
|
21474
22532
|
try {
|
|
21475
22533
|
key = fs22.realpathSync.native(key);
|
|
21476
22534
|
} catch {}
|
|
@@ -21565,7 +22623,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
21565
22623
|
checkout,
|
|
21566
22624
|
heartbeatAt: now.toISOString()
|
|
21567
22625
|
};
|
|
21568
|
-
fs22.mkdirSync(
|
|
22626
|
+
fs22.mkdirSync(path31.dirname(file), { recursive: true });
|
|
21569
22627
|
const temp = `${file}.${process.pid}.tmp`;
|
|
21570
22628
|
fs22.writeFileSync(temp, JSON.stringify(marker));
|
|
21571
22629
|
fs22.renameSync(temp, file);
|
|
@@ -21577,22 +22635,22 @@ var NONE3 = { kind: "none" };
|
|
|
21577
22635
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
21578
22636
|
var canonical2 = (file) => {
|
|
21579
22637
|
let rest = "";
|
|
21580
|
-
for (let dir =
|
|
22638
|
+
for (let dir = path32.resolve(file);; dir = path32.dirname(dir)) {
|
|
21581
22639
|
try {
|
|
21582
|
-
return
|
|
22640
|
+
return path32.join(realpathSync6.native(dir), rest);
|
|
21583
22641
|
} catch {
|
|
21584
|
-
if (
|
|
21585
|
-
return
|
|
21586
|
-
rest = rest ?
|
|
22642
|
+
if (path32.dirname(dir) === dir)
|
|
22643
|
+
return path32.resolve(file);
|
|
22644
|
+
rest = rest ? path32.join(path32.basename(dir), rest) : path32.basename(dir);
|
|
21587
22645
|
}
|
|
21588
22646
|
}
|
|
21589
22647
|
};
|
|
21590
22648
|
var exempt = (cwd, root, file, plans) => {
|
|
21591
22649
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
21592
|
-
const relative =
|
|
21593
|
-
if (relative.startsWith("..") ||
|
|
22650
|
+
const relative = path32.relative(fold(canonical2(root)), fold(canonical2(path32.resolve(cwd, file))));
|
|
22651
|
+
if (relative.startsWith("..") || path32.isAbsolute(relative))
|
|
21594
22652
|
return true;
|
|
21595
|
-
const posix = relative.split(
|
|
22653
|
+
const posix = relative.split(path32.sep).join("/");
|
|
21596
22654
|
const segments = posix.split("/");
|
|
21597
22655
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
21598
22656
|
};
|
|
@@ -21769,10 +22827,10 @@ var linkSources = (args, at) => {
|
|
|
21769
22827
|
const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
|
|
21770
22828
|
const target = dest === undefined ? null : at(dest);
|
|
21771
22829
|
const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
|
|
21772
|
-
const base = target === null ? null : into >= 0 || sources.length > 1 ||
|
|
22830
|
+
const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync10(target) || /[\\/]$/.test(dest ?? "") ? target : path32.dirname(target);
|
|
21773
22831
|
return sources.flatMap((source) => {
|
|
21774
|
-
if (symbolic && base !== null && !
|
|
21775
|
-
return [
|
|
22832
|
+
if (symbolic && base !== null && !path32.isAbsolute(homeExpanded(source)))
|
|
22833
|
+
return [path32.resolve(base, source)];
|
|
21776
22834
|
const resolved = at(source);
|
|
21777
22835
|
return resolved ? [resolved] : [];
|
|
21778
22836
|
});
|
|
@@ -21801,7 +22859,7 @@ var shellWrites = (command, depth = 0) => {
|
|
|
21801
22859
|
return { writes: true, targets: unknown ? null : targets };
|
|
21802
22860
|
};
|
|
21803
22861
|
var cache2 = new Map;
|
|
21804
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
22862
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path32.resolve(root, file)) ?? "-").join("\x00");
|
|
21805
22863
|
var gateFor = (cwd) => {
|
|
21806
22864
|
const store = new TaskStore(cwd);
|
|
21807
22865
|
const listed = store.listTaskIndex();
|
|
@@ -21880,13 +22938,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
|
|
|
21880
22938
|
return [];
|
|
21881
22939
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
21882
22940
|
return [];
|
|
21883
|
-
return [
|
|
22941
|
+
return [path32.resolve(cwd, invocation.dir ?? ".")];
|
|
21884
22942
|
});
|
|
21885
22943
|
};
|
|
21886
22944
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
21887
22945
|
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
21888
22946
|
var POPD2 = new Set(["popd", "pop-location"]);
|
|
21889
|
-
var UNKNOWN_DIR =
|
|
22947
|
+
var UNKNOWN_DIR = path32.resolve(path32.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
21890
22948
|
var cdInto = (dir, words, strict = false) => {
|
|
21891
22949
|
const lost = strict ? UNKNOWN_DIR : undefined;
|
|
21892
22950
|
const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
@@ -21895,7 +22953,7 @@ var cdInto = (dir, words, strict = false) => {
|
|
|
21895
22953
|
return lost;
|
|
21896
22954
|
if (dir === undefined)
|
|
21897
22955
|
return lost;
|
|
21898
|
-
return target === undefined ? os10.homedir() :
|
|
22956
|
+
return target === undefined ? os10.homedir() : path32.resolve(dir, target);
|
|
21899
22957
|
};
|
|
21900
22958
|
var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
21901
22959
|
var homeExpanded = (file) => {
|
|
@@ -21909,7 +22967,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
21909
22967
|
const groups = [];
|
|
21910
22968
|
const at = (file) => {
|
|
21911
22969
|
const home = homeExpanded(file);
|
|
21912
|
-
return
|
|
22970
|
+
return path32.isAbsolute(home) ? home : dir === undefined ? null : path32.resolve(dir, home);
|
|
21913
22971
|
};
|
|
21914
22972
|
for (const segment of segmentsOf(command, dialect)) {
|
|
21915
22973
|
if (segment.group === "open") {
|
|
@@ -21940,7 +22998,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
21940
22998
|
}
|
|
21941
22999
|
const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
|
|
21942
23000
|
if (inner !== null && dir !== undefined)
|
|
21943
|
-
targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
|
|
23001
|
+
targets.push(...segmentTargets(workitRunDir(dir, words), inner, "posix", depth + 1, strict));
|
|
21944
23002
|
if (strict && dir !== undefined) {
|
|
21945
23003
|
const runIn = dir;
|
|
21946
23004
|
for (const sub of depth < 4 ? segment.subs ?? [] : [])
|
|
@@ -21949,7 +23007,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
21949
23007
|
for (const file of opaque ?? []) {
|
|
21950
23008
|
const resolved = at(file);
|
|
21951
23009
|
if (resolved)
|
|
21952
|
-
targets.push(file === "." ?
|
|
23010
|
+
targets.push(file === "." ? path32.join(resolved, DIR_TARGET) : resolved);
|
|
21953
23011
|
}
|
|
21954
23012
|
const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
|
|
21955
23013
|
if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
|
|
@@ -21964,7 +23022,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
21964
23022
|
const edits = editsOf(segment.words);
|
|
21965
23023
|
if (edits === null) {
|
|
21966
23024
|
if (dir !== undefined)
|
|
21967
|
-
targets.push(
|
|
23025
|
+
targets.push(path32.join(dir, DIR_TARGET));
|
|
21968
23026
|
} else
|
|
21969
23027
|
for (const file of edits) {
|
|
21970
23028
|
const resolved = at(file);
|
|
@@ -21975,20 +23033,28 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
|
21975
23033
|
for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
|
|
21976
23034
|
const { at: runsIn, args } = workitCwd(dir, invocation);
|
|
21977
23035
|
if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
|
|
21978
|
-
targets.push(
|
|
23036
|
+
targets.push(path32.join(runsIn, DIR_TARGET));
|
|
21979
23037
|
}
|
|
21980
23038
|
}
|
|
21981
23039
|
return targets;
|
|
21982
23040
|
};
|
|
23041
|
+
var workitRunDir = (dir, words) => {
|
|
23042
|
+
for (const invocation of workitInvocations(words.map(quoteWord2).join(" "), "posix")) {
|
|
23043
|
+
const { at, args } = workitCwd(dir, invocation);
|
|
23044
|
+
if (args[0] === "check" || args[0] === "lock" && args[1] === "run")
|
|
23045
|
+
return at;
|
|
23046
|
+
}
|
|
23047
|
+
return dir;
|
|
23048
|
+
};
|
|
21983
23049
|
var workitCwd = (dir, args) => {
|
|
21984
23050
|
const { rest, cwd } = skipWorkitGlobals(args);
|
|
21985
|
-
return { at: cwd === null ? dir :
|
|
23051
|
+
return { at: cwd === null ? dir : path32.resolve(dir, homeExpanded(cwd)), args: rest };
|
|
21986
23052
|
};
|
|
21987
23053
|
var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
|
|
21988
23054
|
try {
|
|
21989
23055
|
return [
|
|
21990
23056
|
...segmentTargets(cwd, command, dialect, 0, strict),
|
|
21991
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
23057
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path32.join(dir, DIR_TARGET))
|
|
21992
23058
|
];
|
|
21993
23059
|
} catch {
|
|
21994
23060
|
return [];
|
|
@@ -22025,7 +23091,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
|
22025
23091
|
var guarded = (targets) => {
|
|
22026
23092
|
const seen = new Set;
|
|
22027
23093
|
return targets.filter((target) => {
|
|
22028
|
-
const dir =
|
|
23094
|
+
const dir = path32.dirname(target);
|
|
22029
23095
|
if (seen.has(dir))
|
|
22030
23096
|
return false;
|
|
22031
23097
|
seen.add(dir);
|
|
@@ -22046,7 +23112,7 @@ var claimAll = (owner, targets, deps) => {
|
|
|
22046
23112
|
for (const target of guarded(targets))
|
|
22047
23113
|
claimLiveSession(target, owner, deps);
|
|
22048
23114
|
};
|
|
22049
|
-
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
23115
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path32.resolve(cwd, file)) : [path32.join(cwd, DIR_TARGET)];
|
|
22050
23116
|
var issuedTarget = (cwd, targets) => {
|
|
22051
23117
|
const state = resolveStateDir();
|
|
22052
23118
|
const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
|
|
@@ -22054,7 +23120,7 @@ var issuedTarget = (cwd, targets) => {
|
|
|
22054
23120
|
};
|
|
22055
23121
|
var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
|
|
22056
23122
|
const win = platform === "win32";
|
|
22057
|
-
const p = win ?
|
|
23123
|
+
const p = win ? path32.win32 : path32.posix;
|
|
22058
23124
|
const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
|
|
22059
23125
|
const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
|
|
22060
23126
|
const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
|
|
@@ -22145,6 +23211,24 @@ function beforeWriteGate(cwd, paths) {
|
|
|
22145
23211
|
}
|
|
22146
23212
|
// packages/workit-core/src/hooks/stop.ts
|
|
22147
23213
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
23214
|
+
|
|
23215
|
+
// packages/workit-core/src/core/policy/current-judgment.ts
|
|
23216
|
+
function currentJudgment(cwd) {
|
|
23217
|
+
try {
|
|
23218
|
+
const store = new TaskStore(cwd);
|
|
23219
|
+
const listed = store.listTaskIndex();
|
|
23220
|
+
const key = store.currentKey();
|
|
23221
|
+
if (!listed.ok || !key.ok)
|
|
23222
|
+
return null;
|
|
23223
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
23224
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
23225
|
+
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
23226
|
+
} catch {
|
|
23227
|
+
return null;
|
|
23228
|
+
}
|
|
23229
|
+
}
|
|
23230
|
+
|
|
23231
|
+
// packages/workit-core/src/hooks/stop.ts
|
|
22148
23232
|
var NONE4 = { kind: "none" };
|
|
22149
23233
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
22150
23234
|
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;
|
|
@@ -22237,29 +23321,32 @@ var redChecks = (rows, pr, head, endpoint) => {
|
|
|
22237
23321
|
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)" : ""}.`
|
|
22238
23322
|
};
|
|
22239
23323
|
};
|
|
22240
|
-
var
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
const listed = store.listTaskIndex();
|
|
22244
|
-
const key = store.currentKey();
|
|
22245
|
-
if (!listed.ok || !key.ok)
|
|
22246
|
-
return null;
|
|
22247
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
22248
|
-
const task = entry ? store.readTask(entry.id) : null;
|
|
22249
|
-
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
22250
|
-
} catch {
|
|
22251
|
-
return null;
|
|
22252
|
-
}
|
|
23324
|
+
var prBase = (rows) => {
|
|
23325
|
+
const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
|
|
23326
|
+
return typeof seen?.base === "string" ? seen.base : null;
|
|
22253
23327
|
};
|
|
22254
|
-
var unverified = (cwd, branch, all, descriptor, verification) => {
|
|
23328
|
+
var unverified = (cwd, branch, all, descriptor, verification, head) => {
|
|
22255
23329
|
const check = checkVerdicts(cwd, branch, all);
|
|
22256
23330
|
if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
|
|
22257
23331
|
return null;
|
|
22258
|
-
const judgment =
|
|
23332
|
+
const judgment = currentJudgment(cwd);
|
|
22259
23333
|
const high = judgment?.riskTier === "high";
|
|
22260
|
-
|
|
22261
|
-
|
|
22262
|
-
|
|
23334
|
+
let independent;
|
|
23335
|
+
if (verification === "proportional") {
|
|
23336
|
+
const lane = branchLane(cwd, {
|
|
23337
|
+
base: prBase(all.filter((row) => row.branch === branch)),
|
|
23338
|
+
head,
|
|
23339
|
+
judgment
|
|
23340
|
+
});
|
|
23341
|
+
const need = laneNeed(lane.lane);
|
|
23342
|
+
if (need === "none")
|
|
23343
|
+
return null;
|
|
23344
|
+
independent = need === "non-author";
|
|
23345
|
+
} else {
|
|
23346
|
+
if (judgment && !judgment.behaviorChange && !high)
|
|
23347
|
+
return null;
|
|
23348
|
+
independent = verification === "independent" || high;
|
|
23349
|
+
}
|
|
22263
23350
|
if (!independent && check.review === "self-reviewed")
|
|
22264
23351
|
return null;
|
|
22265
23352
|
const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
|
|
@@ -22306,7 +23393,7 @@ function stopObligation(input, descriptor) {
|
|
|
22306
23393
|
}
|
|
22307
23394
|
if (!delivered || endpoint !== "green" && endpoint !== "merged")
|
|
22308
23395
|
return null;
|
|
22309
|
-
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
|
|
23396
|
+
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
|
|
22310
23397
|
return left ? { ...left, head } : null;
|
|
22311
23398
|
} catch {
|
|
22312
23399
|
return null;
|
|
@@ -22576,9 +23663,9 @@ var WRITE_TOOLS = new Set([
|
|
|
22576
23663
|
]);
|
|
22577
23664
|
// packages/workit-core/src/hooks/standing.ts
|
|
22578
23665
|
import fs23 from "node:fs";
|
|
22579
|
-
import
|
|
23666
|
+
import path33 from "node:path";
|
|
22580
23667
|
var readPlans = (store) => {
|
|
22581
|
-
const dir =
|
|
23668
|
+
const dir = path33.join(store, "fanouts");
|
|
22582
23669
|
let names;
|
|
22583
23670
|
try {
|
|
22584
23671
|
names = fs23.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -22588,7 +23675,7 @@ var readPlans = (store) => {
|
|
|
22588
23675
|
const plans = [];
|
|
22589
23676
|
for (const name of names) {
|
|
22590
23677
|
try {
|
|
22591
|
-
const raw = JSON.parse(fs23.readFileSync(
|
|
23678
|
+
const raw = JSON.parse(fs23.readFileSync(path33.join(dir, name), "utf8"));
|
|
22592
23679
|
if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
|
|
22593
23680
|
continue;
|
|
22594
23681
|
const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
@@ -22676,6 +23763,10 @@ var standingSinceStart = (input) => {
|
|
|
22676
23763
|
}
|
|
22677
23764
|
};
|
|
22678
23765
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
23766
|
+
var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
|
|
23767
|
+
var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
|
|
23768
|
+
var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
|
|
23769
|
+
var VERBS = new Set(WORKIT_ALLOWED_VERBS);
|
|
22679
23770
|
var FILTERS = new Set(["tail", "head", "grep"]);
|
|
22680
23771
|
|
|
22681
23772
|
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
@@ -22796,11 +23887,11 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
22796
23887
|
]
|
|
22797
23888
|
};
|
|
22798
23889
|
// packages/workit-opencode/src/shared/init-apply.ts
|
|
22799
|
-
import
|
|
23890
|
+
import path38 from "node:path";
|
|
22800
23891
|
|
|
22801
23892
|
// packages/workit-core/src/core/gitignore.ts
|
|
22802
|
-
import { existsSync as
|
|
22803
|
-
import
|
|
23893
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
23894
|
+
import path34 from "node:path";
|
|
22804
23895
|
var GITIGNORE_ENTRIES = [
|
|
22805
23896
|
"# workit: SDD working state (never commit)",
|
|
22806
23897
|
"docs/*/sdd/",
|
|
@@ -22820,8 +23911,8 @@ var GITIGNORE_ENTRIES = [
|
|
|
22820
23911
|
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
22821
23912
|
if (!confirmed)
|
|
22822
23913
|
return { ok: false, error: "confirmed: true required" };
|
|
22823
|
-
const file =
|
|
22824
|
-
const existing =
|
|
23914
|
+
const file = path34.join(workspaceRoot, ".gitignore");
|
|
23915
|
+
const existing = existsSync11(file) ? readFileSync7(file, "utf8") : "";
|
|
22825
23916
|
const existingLines = new Set(existing.split(`
|
|
22826
23917
|
`).map((l) => l.trim()).filter(Boolean));
|
|
22827
23918
|
const added = [];
|
|
@@ -22840,33 +23931,33 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
|
22840
23931
|
` : "") + append.join(`
|
|
22841
23932
|
`) + `
|
|
22842
23933
|
`, "utf8");
|
|
22843
|
-
} else if (!
|
|
23934
|
+
} else if (!existsSync11(file)) {
|
|
22844
23935
|
writeFileSync4(file, "", "utf8");
|
|
22845
23936
|
}
|
|
22846
23937
|
return { ok: true, path: file, added };
|
|
22847
23938
|
};
|
|
22848
23939
|
|
|
22849
23940
|
// packages/workit-core/src/core/hygiene.ts
|
|
22850
|
-
import { existsSync as
|
|
22851
|
-
import
|
|
23941
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
23942
|
+
import path35 from "node:path";
|
|
22852
23943
|
var repoRoot3 = assetRoot();
|
|
22853
|
-
var templatesDir = () =>
|
|
23944
|
+
var templatesDir = () => path35.join(repoRoot3, "templates", "hygiene");
|
|
22854
23945
|
var packageJson = (root) => {
|
|
22855
|
-
if (!
|
|
23946
|
+
if (!existsSync12(path35.join(root, "package.json")))
|
|
22856
23947
|
return null;
|
|
22857
23948
|
try {
|
|
22858
|
-
return JSON.parse(
|
|
23949
|
+
return JSON.parse(readFileSync8(path35.join(root, "package.json"), "utf8"));
|
|
22859
23950
|
} catch {
|
|
22860
23951
|
return null;
|
|
22861
23952
|
}
|
|
22862
23953
|
};
|
|
22863
23954
|
var isOpenSource = (root) => {
|
|
22864
|
-
if (
|
|
23955
|
+
if (existsSync12(path35.join(root, "LICENSE")))
|
|
22865
23956
|
return true;
|
|
22866
23957
|
const pkg = packageJson(root);
|
|
22867
23958
|
if (pkg && !pkg.private)
|
|
22868
23959
|
return true;
|
|
22869
|
-
return
|
|
23960
|
+
return path35.basename(root) === "workflow-toolkit";
|
|
22870
23961
|
};
|
|
22871
23962
|
var licenseHolder = (root) => {
|
|
22872
23963
|
const pkg = packageJson(root);
|
|
@@ -22889,13 +23980,13 @@ var planHygieneFiles = (root, opts = {}) => {
|
|
|
22889
23980
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
22890
23981
|
const planned = [];
|
|
22891
23982
|
for (const file of files) {
|
|
22892
|
-
if (
|
|
23983
|
+
if (existsSync12(path35.join(root, file)))
|
|
22893
23984
|
continue;
|
|
22894
|
-
const tpl =
|
|
22895
|
-
if (!
|
|
23985
|
+
const tpl = path35.join(templatesDir(), file);
|
|
23986
|
+
if (!existsSync12(tpl))
|
|
22896
23987
|
continue;
|
|
22897
|
-
const content =
|
|
22898
|
-
planned.push({ path:
|
|
23988
|
+
const content = readFileSync8(tpl, "utf8").replace(/<PROJECT>/g, path35.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
23989
|
+
planned.push({ path: path35.join(root, file), content });
|
|
22899
23990
|
}
|
|
22900
23991
|
return planned;
|
|
22901
23992
|
};
|
|
@@ -22905,30 +23996,30 @@ var ensureHygieneFiles = (root, opts) => {
|
|
|
22905
23996
|
const created = [];
|
|
22906
23997
|
for (const planned of planHygieneFiles(root, opts)) {
|
|
22907
23998
|
writeFileSync5(planned.path, planned.content, "utf8");
|
|
22908
|
-
created.push(
|
|
23999
|
+
created.push(path35.basename(planned.path));
|
|
22909
24000
|
}
|
|
22910
24001
|
return { ok: true, created };
|
|
22911
24002
|
};
|
|
22912
24003
|
|
|
22913
24004
|
// packages/workit-core/src/core/init.ts
|
|
22914
24005
|
import fs24 from "node:fs";
|
|
22915
|
-
import
|
|
24006
|
+
import path37 from "node:path";
|
|
22916
24007
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
22917
24008
|
|
|
22918
24009
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
22919
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
22920
|
-
import
|
|
24010
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
24011
|
+
import path36 from "node:path";
|
|
22921
24012
|
import { isDeepStrictEqual } from "node:util";
|
|
22922
24013
|
var readFileSafe = (p) => {
|
|
22923
24014
|
try {
|
|
22924
|
-
return
|
|
24015
|
+
return readFileSync9(p, "utf8");
|
|
22925
24016
|
} catch {
|
|
22926
24017
|
return null;
|
|
22927
24018
|
}
|
|
22928
24019
|
};
|
|
22929
24020
|
function applyWorkspaceBranchPolicy(opts) {
|
|
22930
24021
|
const { workspace_root, env = process.env } = opts;
|
|
22931
|
-
const dir =
|
|
24022
|
+
const dir = path36.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22932
24023
|
const {
|
|
22933
24024
|
status,
|
|
22934
24025
|
path: wsPath,
|
|
@@ -22940,7 +24031,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22940
24031
|
if (status === "malformed" || status === "invalid")
|
|
22941
24032
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
22942
24033
|
const detection = detectBranchPolicy(workspace_root);
|
|
22943
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
24034
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path36.basename(workspace_root));
|
|
22944
24035
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
22945
24036
|
const policy = {
|
|
22946
24037
|
preset: detection.preset,
|
|
@@ -22989,7 +24080,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22989
24080
|
ok: false,
|
|
22990
24081
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
22991
24082
|
};
|
|
22992
|
-
mkdirSync4(
|
|
24083
|
+
mkdirSync4(path36.dirname(wsPath), { recursive: true });
|
|
22993
24084
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
22994
24085
|
try {
|
|
22995
24086
|
writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
|
|
@@ -23054,7 +24145,7 @@ var youtrackJsonContent = (dir) => {
|
|
|
23054
24145
|
const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
|
|
23055
24146
|
return {
|
|
23056
24147
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
|
|
23057
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
24148
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path37.join(dir, "youtrack.token"),
|
|
23058
24149
|
...meetingIssue ? {
|
|
23059
24150
|
meetingIssue,
|
|
23060
24151
|
meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
|
|
@@ -23087,16 +24178,16 @@ function initApplyData(action, env = process.env) {
|
|
|
23087
24178
|
fs24.mkdirSync(dir, { recursive: true });
|
|
23088
24179
|
switch (action) {
|
|
23089
24180
|
case "youtrack_json": {
|
|
23090
|
-
const out =
|
|
24181
|
+
const out = path37.join(dir, "youtrack.json");
|
|
23091
24182
|
fs24.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
23092
24183
|
`, "utf8");
|
|
23093
24184
|
return { action, ok: true, path: out };
|
|
23094
24185
|
}
|
|
23095
24186
|
case "youtrack_token_placeholder": {
|
|
23096
|
-
const p =
|
|
24187
|
+
const p = path37.join(dir, "youtrack.token");
|
|
23097
24188
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
|
|
23098
24189
|
`, 384) === "preserved";
|
|
23099
|
-
const abs =
|
|
24190
|
+
const abs = path37.resolve(p);
|
|
23100
24191
|
return {
|
|
23101
24192
|
action,
|
|
23102
24193
|
ok: true,
|
|
@@ -23108,14 +24199,14 @@ function initApplyData(action, env = process.env) {
|
|
|
23108
24199
|
};
|
|
23109
24200
|
}
|
|
23110
24201
|
case "youtrack_scaffold": {
|
|
23111
|
-
const jsonOut =
|
|
23112
|
-
const tokenOut =
|
|
24202
|
+
const jsonOut = path37.join(dir, "youtrack.json");
|
|
24203
|
+
const tokenOut = path37.join(dir, "youtrack.token");
|
|
23113
24204
|
fs24.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
23114
24205
|
`, "utf8");
|
|
23115
24206
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
|
|
23116
24207
|
`, 384) === "preserved";
|
|
23117
|
-
const configPath =
|
|
23118
|
-
const tokenPath =
|
|
24208
|
+
const configPath = path37.resolve(jsonOut);
|
|
24209
|
+
const tokenPath = path37.resolve(tokenOut);
|
|
23119
24210
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
23120
24211
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
23121
24212
|
let tokenCreate = {};
|
|
@@ -23167,10 +24258,10 @@ function initApplyData(action, env = process.env) {
|
|
|
23167
24258
|
}
|
|
23168
24259
|
}
|
|
23169
24260
|
case "vcs_scaffold": {
|
|
23170
|
-
const jsonOut =
|
|
24261
|
+
const jsonOut = path37.join(dir, "vcs.json");
|
|
23171
24262
|
fs24.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
|
|
23172
24263
|
`, "utf8");
|
|
23173
|
-
const configPath =
|
|
24264
|
+
const configPath = path37.resolve(jsonOut);
|
|
23174
24265
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
23175
24266
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
23176
24267
|
try {
|
|
@@ -23289,7 +24380,7 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
23289
24380
|
protectedNames: args.branch_policy_protected
|
|
23290
24381
|
}, current);
|
|
23291
24382
|
writeConfig(next);
|
|
23292
|
-
return output(ok({ action: "config", path:
|
|
24383
|
+
return output(ok({ action: "config", path: path38.join(configDir(), "config.json"), ...next }));
|
|
23293
24384
|
}
|
|
23294
24385
|
const env = Object.fromEntries(Object.entries({
|
|
23295
24386
|
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
@@ -23306,14 +24397,14 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
23306
24397
|
|
|
23307
24398
|
// packages/workit-opencode/src/shared/session.ts
|
|
23308
24399
|
import { realpathSync as realpathSync7 } from "node:fs";
|
|
23309
|
-
import
|
|
24400
|
+
import path39 from "node:path";
|
|
23310
24401
|
var sameWorkspace = (expected, observed) => {
|
|
23311
24402
|
if (typeof observed !== "string" || !observed)
|
|
23312
24403
|
return false;
|
|
23313
24404
|
try {
|
|
23314
24405
|
return realpathSync7(expected) === realpathSync7(observed);
|
|
23315
24406
|
} catch {
|
|
23316
|
-
return
|
|
24407
|
+
return path39.resolve(expected) === path39.resolve(observed);
|
|
23317
24408
|
}
|
|
23318
24409
|
};
|
|
23319
24410
|
|
|
@@ -23385,7 +24476,7 @@ var workitFamilyOf = (toolName) => {
|
|
|
23385
24476
|
|
|
23386
24477
|
// packages/workit-opencode/src/tools/context.ts
|
|
23387
24478
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
23388
|
-
import
|
|
24479
|
+
import path40 from "node:path";
|
|
23389
24480
|
var createContextTool = () => ({
|
|
23390
24481
|
description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
|
|
23391
24482
|
execute: async (args, context) => {
|
|
@@ -23395,7 +24486,7 @@ var createContextTool = () => ({
|
|
|
23395
24486
|
let root;
|
|
23396
24487
|
try {
|
|
23397
24488
|
const cwd = parsed.data.cwd;
|
|
23398
|
-
root = realpathSync8(cwd ?
|
|
24489
|
+
root = realpathSync8(cwd ? path40.resolve(context.directory, cwd) : context.directory);
|
|
23399
24490
|
} catch {
|
|
23400
24491
|
return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
|
|
23401
24492
|
}
|
|
@@ -23427,18 +24518,18 @@ var workerCoordinatorFor = (task, worker) => {
|
|
|
23427
24518
|
// packages/workit-core/src/fanout.ts
|
|
23428
24519
|
import { createHash as createHash17 } from "node:crypto";
|
|
23429
24520
|
import fs26 from "node:fs";
|
|
23430
|
-
import
|
|
24521
|
+
import path43 from "node:path";
|
|
23431
24522
|
|
|
23432
24523
|
// packages/workit-core/src/stack.ts
|
|
23433
24524
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
23434
24525
|
import { createHash as createHash16, randomBytes as randomBytes4 } from "node:crypto";
|
|
23435
24526
|
import fs25 from "node:fs";
|
|
23436
|
-
import
|
|
24527
|
+
import path42 from "node:path";
|
|
23437
24528
|
|
|
23438
24529
|
// packages/workit-core/src/git/ops.ts
|
|
23439
24530
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
23440
|
-
import { existsSync as
|
|
23441
|
-
import
|
|
24531
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
|
|
24532
|
+
import path41 from "node:path";
|
|
23442
24533
|
|
|
23443
24534
|
// packages/workit-core/src/forge/redact.ts
|
|
23444
24535
|
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_.-]*`;
|
|
@@ -23482,18 +24573,18 @@ function operationInProgress(cwd) {
|
|
|
23482
24573
|
if (!run.ok)
|
|
23483
24574
|
return null;
|
|
23484
24575
|
const found = run.stdout.split(`
|
|
23485
|
-
`).map((line) =>
|
|
24576
|
+
`).map((line) => path41.resolve(cwd, line.trim()));
|
|
23486
24577
|
const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
|
|
23487
24578
|
for (const [index, [, operation]] of markers.entries()) {
|
|
23488
24579
|
const marker = found[index];
|
|
23489
|
-
if (!marker || !
|
|
24580
|
+
if (!marker || !existsSync13(marker))
|
|
23490
24581
|
continue;
|
|
23491
|
-
if (index === 1 &&
|
|
24582
|
+
if (index === 1 && existsSync13(path41.join(marker, "applying")))
|
|
23492
24583
|
return { operation: "am", commitAllowed: false, branch: null };
|
|
23493
|
-
if (index === 0 &&
|
|
24584
|
+
if (index === 0 && existsSync13(path41.join(marker, "amend")) && !conflicted()) {
|
|
23494
24585
|
let headName = "";
|
|
23495
24586
|
try {
|
|
23496
|
-
headName =
|
|
24587
|
+
headName = readFileSync10(path41.join(marker, "head-name"), "utf8").trim();
|
|
23497
24588
|
} catch {}
|
|
23498
24589
|
const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
|
|
23499
24590
|
return { operation, commitAllowed: branch !== null, branch };
|
|
@@ -23620,7 +24711,7 @@ function rebaseInProgress(dir) {
|
|
|
23620
24711
|
}
|
|
23621
24712
|
var scratchDir = (cwd) => {
|
|
23622
24713
|
const dir = stacksDir(cwd);
|
|
23623
|
-
return dir.ok ?
|
|
24714
|
+
return dir.ok ? path42.join(dir.data, "worktrees") : null;
|
|
23624
24715
|
};
|
|
23625
24716
|
function gitRestack(cwd, input) {
|
|
23626
24717
|
const owner = worktreeOf(cwd, input.branch);
|
|
@@ -23630,7 +24721,7 @@ function gitRestack(cwd, input) {
|
|
|
23630
24721
|
if (!scratch)
|
|
23631
24722
|
return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
|
|
23632
24723
|
fs25.mkdirSync(scratch, { recursive: true });
|
|
23633
|
-
dir =
|
|
24724
|
+
dir = path42.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
|
|
23634
24725
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
23635
24726
|
if (!added.ok)
|
|
23636
24727
|
return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
|
|
@@ -23674,7 +24765,7 @@ function stacksDir(cwd) {
|
|
|
23674
24765
|
const root = storeRoot(cwd);
|
|
23675
24766
|
if (!root.ok)
|
|
23676
24767
|
return stackFail(root.code, root.error, root.unblock);
|
|
23677
|
-
return { ok: true, data:
|
|
24768
|
+
return { ok: true, data: path42.join(root.value.root, "stacks") };
|
|
23678
24769
|
}
|
|
23679
24770
|
var HEARTBEAT_STALE_MS = 2 * 60000;
|
|
23680
24771
|
|
|
@@ -23785,7 +24876,7 @@ function fanoutsDir(cwd) {
|
|
|
23785
24876
|
const root = storeRoot(cwd);
|
|
23786
24877
|
if (!root.ok)
|
|
23787
24878
|
return fanoutFail(root.code, root.error, root.unblock);
|
|
23788
|
-
return { ok: true, data:
|
|
24879
|
+
return { ok: true, data: path43.join(root.value.root, "fanouts") };
|
|
23789
24880
|
}
|
|
23790
24881
|
function parseStored(raw) {
|
|
23791
24882
|
if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
|
|
@@ -23853,7 +24944,7 @@ function readFanout(cwd, name) {
|
|
|
23853
24944
|
const dir = fanoutsDir(cwd);
|
|
23854
24945
|
if (!dir.ok)
|
|
23855
24946
|
return dir;
|
|
23856
|
-
const file =
|
|
24947
|
+
const file = path43.join(dir.data, stackFileName(name));
|
|
23857
24948
|
let parsed = null;
|
|
23858
24949
|
try {
|
|
23859
24950
|
parsed = parseStored(JSON.parse(fs26.readFileSync(file, "utf8")));
|
|
@@ -23878,7 +24969,7 @@ function listFanouts(cwd) {
|
|
|
23878
24969
|
for (const entry of names) {
|
|
23879
24970
|
let parsed = null;
|
|
23880
24971
|
try {
|
|
23881
|
-
parsed = parseStored(JSON.parse(fs26.readFileSync(
|
|
24972
|
+
parsed = parseStored(JSON.parse(fs26.readFileSync(path43.join(dir.data, entry), "utf8")));
|
|
23882
24973
|
} catch {
|
|
23883
24974
|
parsed = null;
|
|
23884
24975
|
}
|
|
@@ -24548,6 +25639,8 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
|
|
|
24548
25639
|
return deny(plan.reason);
|
|
24549
25640
|
}
|
|
24550
25641
|
}
|
|
25642
|
+
if (event.effect === "ask" && resources.length > 0 && resources.length === event.resources.length && resources.every(isAllowedWorkitCommand))
|
|
25643
|
+
event.effect = "allow";
|
|
24551
25644
|
};
|
|
24552
25645
|
|
|
24553
25646
|
// packages/workit-opencode/src/v2/child-identity.ts
|
|
@@ -24570,7 +25663,7 @@ var childTaskMissing = (store, input) => {
|
|
|
24570
25663
|
var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only inspect the task summary (workit_task action "inspect") or record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
|
|
24571
25664
|
|
|
24572
25665
|
// packages/workit-opencode/src/v2/shell.ts
|
|
24573
|
-
import
|
|
25666
|
+
import path44 from "node:path";
|
|
24574
25667
|
var commandOf = (input) => {
|
|
24575
25668
|
if (input === null || typeof input !== "object")
|
|
24576
25669
|
return null;
|
|
@@ -24579,7 +25672,7 @@ var commandOf = (input) => {
|
|
|
24579
25672
|
};
|
|
24580
25673
|
var hookInput = (root, sessionID, id, shell) => ({
|
|
24581
25674
|
host: "opencode",
|
|
24582
|
-
cwd: shell.workdir ?
|
|
25675
|
+
cwd: shell.workdir ? path44.resolve(root, shell.workdir) : root,
|
|
24583
25676
|
session: { id: sessionID, agentId: null, agentType: null, parentId: null },
|
|
24584
25677
|
permissionMode: null,
|
|
24585
25678
|
transcriptPath: null,
|
|
@@ -24785,38 +25878,38 @@ var createStopControl = (root, port) => {
|
|
|
24785
25878
|
};
|
|
24786
25879
|
|
|
24787
25880
|
// packages/workit-opencode/src/v2/registry.ts
|
|
24788
|
-
import { existsSync as
|
|
24789
|
-
import
|
|
25881
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
|
|
25882
|
+
import path46 from "node:path";
|
|
24790
25883
|
|
|
24791
25884
|
// packages/workit-opencode/src/shared/assets.ts
|
|
24792
|
-
import { existsSync as
|
|
24793
|
-
import
|
|
25885
|
+
import { existsSync as existsSync14, realpathSync as realpathSync9 } from "node:fs";
|
|
25886
|
+
import path45 from "node:path";
|
|
24794
25887
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
24795
25888
|
var moduleDir = () => {
|
|
24796
25889
|
try {
|
|
24797
|
-
return realpathSync9(
|
|
25890
|
+
return realpathSync9(path45.dirname(fileURLToPath2(import.meta.url)));
|
|
24798
25891
|
} catch {
|
|
24799
|
-
return
|
|
25892
|
+
return path45.dirname(fileURLToPath2(import.meta.url));
|
|
24800
25893
|
}
|
|
24801
25894
|
};
|
|
24802
25895
|
var packageRoot2 = () => {
|
|
24803
25896
|
const dir = moduleDir();
|
|
24804
25897
|
for (const candidate of [
|
|
24805
|
-
|
|
24806
|
-
|
|
24807
|
-
|
|
25898
|
+
path45.resolve(dir, ".."),
|
|
25899
|
+
path45.resolve(dir, "..", ".."),
|
|
25900
|
+
path45.resolve(dir, "..", "..", "..")
|
|
24808
25901
|
]) {
|
|
24809
|
-
if (
|
|
25902
|
+
if (existsSync14(path45.join(candidate, "package.json")))
|
|
24810
25903
|
return candidate;
|
|
24811
25904
|
}
|
|
24812
|
-
return
|
|
25905
|
+
return path45.resolve(dir, "..");
|
|
24813
25906
|
};
|
|
24814
25907
|
var skillsRoot = () => {
|
|
24815
25908
|
const root = packageRoot2();
|
|
24816
|
-
const canonical =
|
|
24817
|
-
if (
|
|
25909
|
+
const canonical = path45.resolve(root, "..", "workit-core", "skills");
|
|
25910
|
+
if (existsSync14(path45.join(root, "src")) && existsSync14(canonical))
|
|
24818
25911
|
return canonical;
|
|
24819
|
-
return
|
|
25912
|
+
return path45.join(root, "assets", "skills");
|
|
24820
25913
|
};
|
|
24821
25914
|
|
|
24822
25915
|
// packages/workit-opencode/src/v2/registry.ts
|
|
@@ -24840,15 +25933,15 @@ var parseFrontmatter = (markdown) => {
|
|
|
24840
25933
|
};
|
|
24841
25934
|
var registerSkills = async (ctx) => {
|
|
24842
25935
|
const skillsDir = skillsRoot();
|
|
24843
|
-
if (!
|
|
25936
|
+
if (!existsSync15(skillsDir))
|
|
24844
25937
|
return new Set;
|
|
24845
25938
|
const listed = await ctx.skill.list().catch(() => ({ data: [] }));
|
|
24846
|
-
const own = (skill) => typeof skill.path === "string" &&
|
|
25939
|
+
const own = (skill) => typeof skill.path === "string" && path46.resolve(skill.path).startsWith(skillsDir + path46.sep);
|
|
24847
25940
|
const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
|
|
24848
25941
|
const packaged = new Map;
|
|
24849
25942
|
for (const id of WORKIT_METHOD_SKILLS) {
|
|
24850
|
-
const file =
|
|
24851
|
-
if (!userSkills.has(id) &&
|
|
25943
|
+
const file = path46.join(skillsDir, id, "SKILL.md");
|
|
25944
|
+
if (!userSkills.has(id) && existsSync15(file))
|
|
24852
25945
|
packaged.set(id, file);
|
|
24853
25946
|
}
|
|
24854
25947
|
await ctx.skill.transform((editor) => {
|
|
@@ -24856,7 +25949,7 @@ var registerSkills = async (ctx) => {
|
|
|
24856
25949
|
for (const [id, file] of packaged) {
|
|
24857
25950
|
if (existing.has(id))
|
|
24858
25951
|
continue;
|
|
24859
|
-
const parsed = parseFrontmatter(
|
|
25952
|
+
const parsed = parseFrontmatter(readFileSync11(file, "utf8"));
|
|
24860
25953
|
editor.add({
|
|
24861
25954
|
id,
|
|
24862
25955
|
name: parsed.name || id,
|
|
@@ -24894,25 +25987,25 @@ var registerCommands = async (ctx, registeredSkills) => {
|
|
|
24894
25987
|
};
|
|
24895
25988
|
|
|
24896
25989
|
// packages/workit-opencode/src/stale-sources.ts
|
|
24897
|
-
import { existsSync as
|
|
24898
|
-
import
|
|
25990
|
+
import { existsSync as existsSync16, readdirSync as readdirSync6 } from "node:fs";
|
|
25991
|
+
import path47 from "node:path";
|
|
24899
25992
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
24900
25993
|
var typescriptFiles = (directory) => {
|
|
24901
|
-
if (!
|
|
25994
|
+
if (!existsSync16(directory))
|
|
24902
25995
|
return [];
|
|
24903
25996
|
return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
24904
|
-
const file =
|
|
25997
|
+
const file = path47.join(directory, entry.name);
|
|
24905
25998
|
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
24906
25999
|
});
|
|
24907
26000
|
};
|
|
24908
26001
|
var pluginSourceFiles = [
|
|
24909
26002
|
...new Set([
|
|
24910
26003
|
fileURLToPath3(import.meta.url),
|
|
24911
|
-
...typescriptFiles(
|
|
24912
|
-
|
|
24913
|
-
...typescriptFiles(
|
|
26004
|
+
...typescriptFiles(path47.join(packageRoot2(), "src")),
|
|
26005
|
+
path47.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
26006
|
+
...typescriptFiles(path47.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
24914
26007
|
])
|
|
24915
|
-
].filter((file) =>
|
|
26008
|
+
].filter((file) => existsSync16(file)).toSorted();
|
|
24916
26009
|
|
|
24917
26010
|
// packages/workit-opencode/src/v2/plugin.ts
|
|
24918
26011
|
var sessionFacts = async (ctx, sessionID) => {
|