@brainervirus/workit-opencode 8.16.1 → 8.17.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 +7 -7
- package/assets/skills/workit-fanout/references/brief.md +3 -1
- package/assets/skills/workit-implement/SKILL.md +8 -1
- package/assets/skills/workit-review/SKILL.md +2 -1
- package/assets/skills/workit-verify-app/SKILL.md +2 -0
- package/dist/plugin.js +1070 -139
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -12398,6 +12398,17 @@ import fs9 from "node:fs";
|
|
|
12398
12398
|
import path9 from "node:path";
|
|
12399
12399
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
12400
12400
|
var CHECKS_FILE = "workit.checks.json";
|
|
12401
|
+
var checksConfigFile = (root) => path9.join(root, CHECKS_FILE);
|
|
12402
|
+
function repoHeavyCommands(root) {
|
|
12403
|
+
try {
|
|
12404
|
+
const value = JSON.parse(fs9.readFileSync(checksConfigFile(root), "utf8"));
|
|
12405
|
+
const resources = isRecord(value) ? value.resources : undefined;
|
|
12406
|
+
const list = isRecord(resources) ? resources.heavyCommands : undefined;
|
|
12407
|
+
return Array.isArray(list) ? list.filter((item) => typeof item === "string") : [];
|
|
12408
|
+
} catch {
|
|
12409
|
+
return [];
|
|
12410
|
+
}
|
|
12411
|
+
}
|
|
12401
12412
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12402
12413
|
var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
|
|
12403
12414
|
function splitCommand(command) {
|
|
@@ -12510,7 +12521,7 @@ function detectedChecks(root) {
|
|
|
12510
12521
|
var GATES = ["testing", "verification"];
|
|
12511
12522
|
function loadCheckConfig(cwd) {
|
|
12512
12523
|
const root = checkRoot(cwd);
|
|
12513
|
-
const file =
|
|
12524
|
+
const file = checksConfigFile(root);
|
|
12514
12525
|
const none = (error = null) => ({
|
|
12515
12526
|
source: error ? CHECKS_FILE : "none",
|
|
12516
12527
|
root,
|
|
@@ -13255,6 +13266,7 @@ var workspaceConfigSchema = object({
|
|
|
13255
13266
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
13256
13267
|
defaultEndpoint: unknown().optional(),
|
|
13257
13268
|
verification: _enum(["self", "independent"]).optional(),
|
|
13269
|
+
delegation: unknown().optional(),
|
|
13258
13270
|
trailers: boolean2().optional(),
|
|
13259
13271
|
liveSessionGuard: boolean2().optional(),
|
|
13260
13272
|
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
@@ -17506,7 +17518,16 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
|
17506
17518
|
return found;
|
|
17507
17519
|
}
|
|
17508
17520
|
var RECENT = new Map;
|
|
17509
|
-
function
|
|
17521
|
+
function commandLines(command, dialect = "posix") {
|
|
17522
|
+
const lines = [];
|
|
17523
|
+
try {
|
|
17524
|
+
walk2(command, dialect, 0, lines);
|
|
17525
|
+
} catch (error) {
|
|
17526
|
+
throw error instanceof ShellAnalysisError ? error : new ShellAnalysisError(error instanceof Error ? error.message : String(error));
|
|
17527
|
+
}
|
|
17528
|
+
return lines;
|
|
17529
|
+
}
|
|
17530
|
+
function walk2(command, dialect, depth, seen) {
|
|
17510
17531
|
const found = [];
|
|
17511
17532
|
let dir = null;
|
|
17512
17533
|
const groups = [];
|
|
@@ -17517,7 +17538,8 @@ function walk2(command, dialect, depth) {
|
|
|
17517
17538
|
const nested = (script, inner) => {
|
|
17518
17539
|
if (!script || depth >= MAX_DEPTH)
|
|
17519
17540
|
return;
|
|
17520
|
-
|
|
17541
|
+
const inside = seen ? walk2(script, inner, depth + 1, seen) : rawInvocations(script, inner, depth + 1);
|
|
17542
|
+
for (const invocation of inside)
|
|
17521
17543
|
found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
|
|
17522
17544
|
};
|
|
17523
17545
|
const visit = (input, stdin, hops) => {
|
|
@@ -17529,11 +17551,17 @@ function walk2(command, dialect, depth) {
|
|
|
17529
17551
|
const name = commandName(words[0]);
|
|
17530
17552
|
if (!name)
|
|
17531
17553
|
return;
|
|
17554
|
+
seen?.push(words);
|
|
17555
|
+
if (name === "-c" || name === "--command")
|
|
17556
|
+
return nested(words[1], "posix");
|
|
17532
17557
|
const runs = (lines) => {
|
|
17533
17558
|
if (hops < MAX_DEPTH)
|
|
17534
17559
|
for (const next of lines)
|
|
17535
17560
|
visit(next, undefined, hops + 1);
|
|
17536
17561
|
};
|
|
17562
|
+
const wrapped = seen ? null : workitCheckTail(words);
|
|
17563
|
+
if (wrapped)
|
|
17564
|
+
return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
|
|
17537
17565
|
if (CD.has(name)) {
|
|
17538
17566
|
const target = cdTarget(words);
|
|
17539
17567
|
dir = target === null ? os4.homedir() : joinDir(dir, target);
|
|
@@ -17624,6 +17652,51 @@ var LAUNCHERS = new Set([
|
|
|
17624
17652
|
"command"
|
|
17625
17653
|
]);
|
|
17626
17654
|
var WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
|
|
17655
|
+
var WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
|
|
17656
|
+
var PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
|
|
17657
|
+
function workitCheckTail(words) {
|
|
17658
|
+
let at = 0;
|
|
17659
|
+
if (PACKAGE_RUNNERS.has(commandName(words[0]))) {
|
|
17660
|
+
at = words.findIndex((word, index) => index > 0 && !word.startsWith("-"));
|
|
17661
|
+
if (at < 0 || !WORKIT_PACKAGE.test(words[at]))
|
|
17662
|
+
return null;
|
|
17663
|
+
} else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
|
|
17664
|
+
return null;
|
|
17665
|
+
const args = words.slice(at + 1);
|
|
17666
|
+
if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
|
|
17667
|
+
return null;
|
|
17668
|
+
const split = args.indexOf("--");
|
|
17669
|
+
const tail = split < 0 ? [] : args.slice(split + 1);
|
|
17670
|
+
if (!tail.length)
|
|
17671
|
+
return null;
|
|
17672
|
+
return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
|
|
17673
|
+
}
|
|
17674
|
+
function simpleCommand(command, dialect = "posix", depth = 0) {
|
|
17675
|
+
let segments;
|
|
17676
|
+
try {
|
|
17677
|
+
segments = segmentsOf(command, dialect);
|
|
17678
|
+
} catch {
|
|
17679
|
+
return null;
|
|
17680
|
+
}
|
|
17681
|
+
if (segments.length !== 1)
|
|
17682
|
+
return null;
|
|
17683
|
+
const [segment] = segments;
|
|
17684
|
+
if (segment.group || segment.pipe || segment.unanalyzable || segment.redirects.length || segment.subs?.length || segment.stdin?.length)
|
|
17685
|
+
return null;
|
|
17686
|
+
const { words } = stripPrefixes(segment.words);
|
|
17687
|
+
const name = commandName(words[0]);
|
|
17688
|
+
if (!name || depth >= MAX_DEPTH)
|
|
17689
|
+
return null;
|
|
17690
|
+
if (SHELLS.has(name)) {
|
|
17691
|
+
const script = shellScript(words, undefined);
|
|
17692
|
+
return script ? simpleCommand(script, "posix", depth + 1) : null;
|
|
17693
|
+
}
|
|
17694
|
+
if (name === "-c" || name === "--command")
|
|
17695
|
+
return words[1] ? simpleCommand(words[1], "posix", depth + 1) : null;
|
|
17696
|
+
if (EVALS.has(name) || POWERSHELLS.has(name) || ["xargs", "find", "parallel", "script", "cmd", "source", "."].includes(name))
|
|
17697
|
+
return null;
|
|
17698
|
+
return words;
|
|
17699
|
+
}
|
|
17627
17700
|
function workitInvocations(command, dialect = "posix", depth = 0) {
|
|
17628
17701
|
const found = [];
|
|
17629
17702
|
for (const { words } of segmentsOf(command, dialect)) {
|
|
@@ -21971,17 +22044,816 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
21971
22044
|
}
|
|
21972
22045
|
return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
|
|
21973
22046
|
};
|
|
21974
|
-
// packages/workit-core/src/
|
|
21975
|
-
import
|
|
22047
|
+
// packages/workit-core/src/core/heavy-rules.ts
|
|
22048
|
+
import fs18 from "node:fs";
|
|
22049
|
+
import os6 from "node:os";
|
|
22050
|
+
import path26 from "node:path";
|
|
22051
|
+
var EMPTY = () => ({ deny: [], allow: [] });
|
|
22052
|
+
var readText2 = (file) => {
|
|
22053
|
+
try {
|
|
22054
|
+
return fs18.readFileSync(file, "utf8");
|
|
22055
|
+
} catch (error) {
|
|
22056
|
+
if (error.code === "ENOENT")
|
|
22057
|
+
return null;
|
|
22058
|
+
throw error;
|
|
22059
|
+
}
|
|
22060
|
+
};
|
|
22061
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22062
|
+
function parseJsonc(text) {
|
|
22063
|
+
let out = "";
|
|
22064
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
22065
|
+
const char = text[index];
|
|
22066
|
+
if (char === '"') {
|
|
22067
|
+
const end = /"(?:[^"\\]|\\.)*"/y;
|
|
22068
|
+
end.lastIndex = index;
|
|
22069
|
+
const match = end.exec(text);
|
|
22070
|
+
if (!match)
|
|
22071
|
+
throw new Error("unterminated string");
|
|
22072
|
+
out += match[0];
|
|
22073
|
+
index += match[0].length - 1;
|
|
22074
|
+
} else if (char === "/" && text[index + 1] === "/") {
|
|
22075
|
+
while (index < text.length && text[index] !== `
|
|
22076
|
+
`)
|
|
22077
|
+
index += 1;
|
|
22078
|
+
out += `
|
|
22079
|
+
`;
|
|
22080
|
+
} else if (char === "/" && text[index + 1] === "*") {
|
|
22081
|
+
const close = text.indexOf("*/", index + 2);
|
|
22082
|
+
if (close < 0)
|
|
22083
|
+
throw new Error("unterminated comment");
|
|
22084
|
+
index = close + 1;
|
|
22085
|
+
} else
|
|
22086
|
+
out += char;
|
|
22087
|
+
}
|
|
22088
|
+
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
22089
|
+
}
|
|
22090
|
+
function gitTop(cwd) {
|
|
22091
|
+
for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
|
|
22092
|
+
if (fs18.existsSync(path26.join(dir, ".git")))
|
|
22093
|
+
return dir;
|
|
22094
|
+
if (path26.dirname(dir) === dir)
|
|
22095
|
+
return null;
|
|
22096
|
+
}
|
|
22097
|
+
}
|
|
22098
|
+
var projectDirs = (cwd) => {
|
|
22099
|
+
const top = gitTop(cwd);
|
|
22100
|
+
const dirs = [];
|
|
22101
|
+
for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
|
|
22102
|
+
dirs.push(dir);
|
|
22103
|
+
if (!top || dir === top || path26.dirname(dir) === dir)
|
|
22104
|
+
return dirs;
|
|
22105
|
+
}
|
|
22106
|
+
};
|
|
22107
|
+
var homeOf = (env) => env.HOME || os6.homedir();
|
|
22108
|
+
var CLAUDE_MANAGED = {
|
|
22109
|
+
linux: "/etc/claude-code/managed-settings.json",
|
|
22110
|
+
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
22111
|
+
win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
|
|
22112
|
+
};
|
|
22113
|
+
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
22114
|
+
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
22115
|
+
const user = env.CLAUDE_CONFIG_DIR || path26.join(homeOf(env), ".claude");
|
|
22116
|
+
return [
|
|
22117
|
+
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
22118
|
+
path26.join(user, "settings.json"),
|
|
22119
|
+
path26.join(project, ".claude", "settings.json"),
|
|
22120
|
+
path26.join(project, ".claude", "settings.local.json")
|
|
22121
|
+
];
|
|
22122
|
+
}
|
|
22123
|
+
var bashPattern = (entry) => {
|
|
22124
|
+
const match = /^Bash(?:\((.*)\))?$/su.exec(entry.trim());
|
|
22125
|
+
return match ? match[1] ?? "" : null;
|
|
22126
|
+
};
|
|
22127
|
+
function claudeRules(cwd, env = process.env) {
|
|
22128
|
+
const rules = EMPTY();
|
|
22129
|
+
try {
|
|
22130
|
+
for (const file of claudeSettingsFiles(cwd, env)) {
|
|
22131
|
+
const text = readText2(file);
|
|
22132
|
+
if (text === null)
|
|
22133
|
+
continue;
|
|
22134
|
+
const value = JSON.parse(text);
|
|
22135
|
+
if (!isRecord4(value))
|
|
22136
|
+
return "unreadable";
|
|
22137
|
+
const permissions = value.permissions;
|
|
22138
|
+
if (permissions === undefined)
|
|
22139
|
+
continue;
|
|
22140
|
+
if (!isRecord4(permissions))
|
|
22141
|
+
return "unreadable";
|
|
22142
|
+
for (const [key, bucket] of [
|
|
22143
|
+
["deny", rules.deny],
|
|
22144
|
+
["ask", rules.deny],
|
|
22145
|
+
["allow", rules.allow]
|
|
22146
|
+
]) {
|
|
22147
|
+
const list = permissions[key];
|
|
22148
|
+
if (list === undefined)
|
|
22149
|
+
continue;
|
|
22150
|
+
if (!Array.isArray(list))
|
|
22151
|
+
return "unreadable";
|
|
22152
|
+
for (const entry of list) {
|
|
22153
|
+
if (typeof entry !== "string")
|
|
22154
|
+
return "unreadable";
|
|
22155
|
+
const pattern = bashPattern(entry);
|
|
22156
|
+
if (pattern !== null)
|
|
22157
|
+
bucket.push(pattern);
|
|
22158
|
+
}
|
|
22159
|
+
}
|
|
22160
|
+
}
|
|
22161
|
+
} catch {
|
|
22162
|
+
return "unreadable";
|
|
22163
|
+
}
|
|
22164
|
+
return rules;
|
|
22165
|
+
}
|
|
22166
|
+
var OPENCODE_NAMES = ["opencode.json", "opencode.jsonc"];
|
|
22167
|
+
var opencodePermission = (permission, rules) => {
|
|
22168
|
+
if (permission === undefined)
|
|
22169
|
+
return true;
|
|
22170
|
+
if (typeof permission === "string") {
|
|
22171
|
+
(permission === "allow" ? rules.allow : rules.deny).push("");
|
|
22172
|
+
return ["allow", "ask", "deny"].includes(permission);
|
|
22173
|
+
}
|
|
22174
|
+
if (!isRecord4(permission))
|
|
22175
|
+
return false;
|
|
22176
|
+
for (const key of ["bash", "shell"]) {
|
|
22177
|
+
const value = permission[key];
|
|
22178
|
+
if (value === undefined)
|
|
22179
|
+
continue;
|
|
22180
|
+
if (typeof value === "string") {
|
|
22181
|
+
if (!["allow", "ask", "deny"].includes(value))
|
|
22182
|
+
return false;
|
|
22183
|
+
(value === "allow" ? rules.allow : rules.deny).push("");
|
|
22184
|
+
continue;
|
|
22185
|
+
}
|
|
22186
|
+
if (!isRecord4(value))
|
|
22187
|
+
return false;
|
|
22188
|
+
for (const [pattern, action] of Object.entries(value)) {
|
|
22189
|
+
if (action !== "allow" && action !== "ask" && action !== "deny")
|
|
22190
|
+
return false;
|
|
22191
|
+
(action === "allow" ? rules.allow : rules.deny).push(pattern);
|
|
22192
|
+
}
|
|
22193
|
+
}
|
|
22194
|
+
return true;
|
|
22195
|
+
};
|
|
22196
|
+
var opencodeConfig = (value, rules) => {
|
|
22197
|
+
if (!isRecord4(value))
|
|
22198
|
+
return false;
|
|
22199
|
+
if (!opencodePermission(value.permission, rules))
|
|
22200
|
+
return false;
|
|
22201
|
+
if (value.agent === undefined)
|
|
22202
|
+
return true;
|
|
22203
|
+
if (!isRecord4(value.agent))
|
|
22204
|
+
return false;
|
|
22205
|
+
return Object.values(value.agent).every((agent) => isRecord4(agent) && opencodePermission(agent.permission, rules));
|
|
22206
|
+
};
|
|
22207
|
+
function opencodeRules(cwd, env = process.env) {
|
|
22208
|
+
const rules = EMPTY();
|
|
22209
|
+
try {
|
|
22210
|
+
const config = env.XDG_CONFIG_HOME || path26.join(homeOf(env), ".config");
|
|
22211
|
+
const files = [
|
|
22212
|
+
...OPENCODE_NAMES.map((name) => path26.join(config, "opencode", name)),
|
|
22213
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path26.join(dir, name), path26.join(dir, ".opencode", name)])),
|
|
22214
|
+
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
22215
|
+
];
|
|
22216
|
+
for (const file of files) {
|
|
22217
|
+
const text = readText2(file);
|
|
22218
|
+
if (text !== null && !opencodeConfig(parseJsonc(text), rules))
|
|
22219
|
+
return "unreadable";
|
|
22220
|
+
}
|
|
22221
|
+
if (env.OPENCODE_CONFIG_CONTENT && !opencodeConfig(parseJsonc(env.OPENCODE_CONFIG_CONTENT), rules))
|
|
22222
|
+
return "unreadable";
|
|
22223
|
+
} catch {
|
|
22224
|
+
return "unreadable";
|
|
22225
|
+
}
|
|
22226
|
+
return rules;
|
|
22227
|
+
}
|
|
22228
|
+
var codexRule = (body) => {
|
|
22229
|
+
const open = /\bpattern\s*=\s*\[/u.exec(body);
|
|
22230
|
+
if (!open)
|
|
22231
|
+
return null;
|
|
22232
|
+
const words = [];
|
|
22233
|
+
let index = open.index + open[0].length;
|
|
22234
|
+
for (;; ) {
|
|
22235
|
+
while (/[\s,]/u.test(body[index] ?? ""))
|
|
22236
|
+
index += 1;
|
|
22237
|
+
if (body[index] === "]")
|
|
22238
|
+
break;
|
|
22239
|
+
if (body[index] !== '"')
|
|
22240
|
+
return null;
|
|
22241
|
+
const literal = /"((?:[^"\\]|\\.)*)"/y;
|
|
22242
|
+
literal.lastIndex = index;
|
|
22243
|
+
const match = literal.exec(body);
|
|
22244
|
+
if (!match)
|
|
22245
|
+
return null;
|
|
22246
|
+
words.push(JSON.parse(`"${match[1]}"`));
|
|
22247
|
+
index = literal.lastIndex;
|
|
22248
|
+
}
|
|
22249
|
+
const decision = /\bdecision\s*=\s*"(\w+)"/u.exec(body)?.[1];
|
|
22250
|
+
if (!words.length || !decision || !["allow", "prompt", "forbidden"].includes(decision))
|
|
22251
|
+
return null;
|
|
22252
|
+
return { pattern: words.join(" "), decision };
|
|
22253
|
+
};
|
|
22254
|
+
function codexRules(cwd, env = process.env) {
|
|
22255
|
+
const rules = EMPTY();
|
|
22256
|
+
try {
|
|
22257
|
+
const home = env.CODEX_HOME || path26.join(homeOf(env), ".codex");
|
|
22258
|
+
const top = gitTop(cwd) ?? cwd;
|
|
22259
|
+
for (const dir of [path26.join(home, "rules"), path26.join(top, ".codex", "rules")]) {
|
|
22260
|
+
let files;
|
|
22261
|
+
try {
|
|
22262
|
+
files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
22263
|
+
} catch (error) {
|
|
22264
|
+
if (error.code === "ENOENT")
|
|
22265
|
+
continue;
|
|
22266
|
+
return "unreadable";
|
|
22267
|
+
}
|
|
22268
|
+
for (const name of files) {
|
|
22269
|
+
const text = readText2(path26.join(dir, name)) ?? "";
|
|
22270
|
+
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
22271
|
+
const rule = codexRule(part);
|
|
22272
|
+
if (!rule)
|
|
22273
|
+
return "unreadable";
|
|
22274
|
+
(rule.decision === "allow" ? rules.allow : rules.deny).push(rule.pattern);
|
|
22275
|
+
}
|
|
22276
|
+
}
|
|
22277
|
+
}
|
|
22278
|
+
} catch {
|
|
22279
|
+
return "unreadable";
|
|
22280
|
+
}
|
|
22281
|
+
return rules;
|
|
22282
|
+
}
|
|
22283
|
+
function cursorRules(cwd, env = process.env) {
|
|
22284
|
+
const rules = EMPTY();
|
|
22285
|
+
try {
|
|
22286
|
+
const top = gitTop(cwd) ?? cwd;
|
|
22287
|
+
for (const file of [
|
|
22288
|
+
path26.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
22289
|
+
path26.join(top, ".cursor", "cli.json")
|
|
22290
|
+
]) {
|
|
22291
|
+
const text = readText2(file);
|
|
22292
|
+
if (text === null)
|
|
22293
|
+
continue;
|
|
22294
|
+
const value = JSON.parse(text);
|
|
22295
|
+
if (!isRecord4(value))
|
|
22296
|
+
return "unreadable";
|
|
22297
|
+
if (value.permissions === undefined)
|
|
22298
|
+
continue;
|
|
22299
|
+
if (!isRecord4(value.permissions))
|
|
22300
|
+
return "unreadable";
|
|
22301
|
+
for (const [key, bucket] of [
|
|
22302
|
+
["deny", rules.deny],
|
|
22303
|
+
["allow", rules.allow]
|
|
22304
|
+
]) {
|
|
22305
|
+
const list = value.permissions[key];
|
|
22306
|
+
if (list === undefined)
|
|
22307
|
+
continue;
|
|
22308
|
+
if (!Array.isArray(list))
|
|
22309
|
+
return "unreadable";
|
|
22310
|
+
for (const entry of list) {
|
|
22311
|
+
if (typeof entry !== "string")
|
|
22312
|
+
return "unreadable";
|
|
22313
|
+
const match = /^Shell(?:\((.*)\))?$/su.exec(entry.trim());
|
|
22314
|
+
if (match)
|
|
22315
|
+
bucket.push(match[1] ?? "");
|
|
22316
|
+
}
|
|
22317
|
+
}
|
|
22318
|
+
}
|
|
22319
|
+
} catch {
|
|
22320
|
+
return "unreadable";
|
|
22321
|
+
}
|
|
22322
|
+
return rules;
|
|
22323
|
+
}
|
|
22324
|
+
var baseName = (word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "");
|
|
22325
|
+
var firstToken = (pattern) => pattern.trim().split(/[:\s]/u)[0] ?? "";
|
|
22326
|
+
function names(pattern, words) {
|
|
22327
|
+
const token = firstToken(pattern);
|
|
22328
|
+
if (!token || token === "*" || token.includes("*"))
|
|
22329
|
+
return true;
|
|
22330
|
+
const wanted = baseName(token);
|
|
22331
|
+
return words.some((word) => baseName(word) === wanted);
|
|
22332
|
+
}
|
|
22333
|
+
var literalPrefix = (pattern) => pattern.trim().replace(/(?::\*| \*|\*)$/u, "");
|
|
22334
|
+
var coversWrapper = (pattern) => {
|
|
22335
|
+
const token = firstToken(pattern);
|
|
22336
|
+
if (!token || token.includes("*"))
|
|
22337
|
+
return true;
|
|
22338
|
+
const words = literalPrefix(pattern).split(/\s+/u);
|
|
22339
|
+
if (baseName(words[0]) !== "workit")
|
|
22340
|
+
return false;
|
|
22341
|
+
return ["check", "--heavy"].slice(0, words.length - 1).every((word, at) => {
|
|
22342
|
+
const given = words[at + 1];
|
|
22343
|
+
return given === word || word.startsWith(given);
|
|
22344
|
+
});
|
|
22345
|
+
};
|
|
22346
|
+
var coversCommand = (pattern, command) => {
|
|
22347
|
+
const prefix = literalPrefix(pattern);
|
|
22348
|
+
if (!prefix)
|
|
22349
|
+
return true;
|
|
22350
|
+
return !prefix.includes("*") && command.trim().startsWith(prefix);
|
|
22351
|
+
};
|
|
22352
|
+
function wrapAllowed(read, words, command, options = {}) {
|
|
22353
|
+
if (read === "unreadable")
|
|
22354
|
+
return { ok: false, why: "named" };
|
|
22355
|
+
if (read.deny.some((pattern) => names(pattern, words)))
|
|
22356
|
+
return { ok: false, why: "named" };
|
|
22357
|
+
if (!options.bypass && read.allow.some(coversWrapper) && !read.allow.some((pattern) => coversCommand(pattern, command)))
|
|
22358
|
+
return { ok: false, why: "allowlisted" };
|
|
22359
|
+
return { ok: true };
|
|
22360
|
+
}
|
|
22361
|
+
|
|
22362
|
+
// packages/workit-core/src/core/heavy.ts
|
|
22363
|
+
import fs19 from "node:fs";
|
|
21976
22364
|
import os7 from "node:os";
|
|
21977
22365
|
import path27 from "node:path";
|
|
22366
|
+
function stateDir(env, platform = process.platform) {
|
|
22367
|
+
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
22368
|
+
return env.WORKFLOW_TOOLKIT_STATE;
|
|
22369
|
+
if (env.XDG_STATE_HOME)
|
|
22370
|
+
return path27.join(env.XDG_STATE_HOME, "workit");
|
|
22371
|
+
const home = env.HOME || os7.homedir();
|
|
22372
|
+
if (platform === "darwin")
|
|
22373
|
+
return path27.join(home, "Library", "Application Support", "workit");
|
|
22374
|
+
if (platform === "win32")
|
|
22375
|
+
return path27.join(env.LOCALAPPDATA ?? path27.join(home, "AppData", "Local"), "workit");
|
|
22376
|
+
return path27.join(home, ".local", "state", "workit");
|
|
22377
|
+
}
|
|
22378
|
+
var HEAVY_COMMANDS_FILE = "heavy-commands.json";
|
|
22379
|
+
var heavyCommandsFile = (env) => path27.join(stateDir(env), HEAVY_COMMANDS_FILE);
|
|
22380
|
+
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;
|
|
22381
|
+
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
22382
|
+
var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
22383
|
+
var WATCH_SCRIPT = /(?:^|[:_.-])(?:watch|dev|serve|start|storybook)(?:$|[:_.-])/iu;
|
|
22384
|
+
var sub = (...names) => new Set(names);
|
|
22385
|
+
var TOOLS = {
|
|
22386
|
+
jest: "any",
|
|
22387
|
+
vitest: "any",
|
|
22388
|
+
mocha: "any",
|
|
22389
|
+
ava: "any",
|
|
22390
|
+
tap: "any",
|
|
22391
|
+
karma: "any",
|
|
22392
|
+
jasmine: "any",
|
|
22393
|
+
nyc: "any",
|
|
22394
|
+
c8: "any",
|
|
22395
|
+
tsc: "any",
|
|
22396
|
+
"vue-tsc": "any",
|
|
22397
|
+
"svelte-check": "any",
|
|
22398
|
+
eslint: "any",
|
|
22399
|
+
stylelint: "any",
|
|
22400
|
+
tslint: "any",
|
|
22401
|
+
webpack: "any",
|
|
22402
|
+
"webpack-cli": "any",
|
|
22403
|
+
rollup: "any",
|
|
22404
|
+
parcel: "any",
|
|
22405
|
+
tsup: "any",
|
|
22406
|
+
vite: sub("build", "optimize"),
|
|
22407
|
+
next: sub("build", "lint"),
|
|
22408
|
+
nuxt: sub("build", "generate"),
|
|
22409
|
+
nuxi: sub("build", "generate"),
|
|
22410
|
+
astro: sub("build", "check"),
|
|
22411
|
+
ng: sub("build", "b", "test", "t", "e2e", "lint"),
|
|
22412
|
+
"react-scripts": sub("build", "test"),
|
|
22413
|
+
playwright: sub("test", "install"),
|
|
22414
|
+
cypress: sub("run"),
|
|
22415
|
+
wdio: "any",
|
|
22416
|
+
testcafe: "any",
|
|
22417
|
+
turbo: "any",
|
|
22418
|
+
lerna: sub("run", "exec", "bootstrap"),
|
|
22419
|
+
"sonar-scanner": "any",
|
|
22420
|
+
sonarless: sub("scan"),
|
|
22421
|
+
"sonarless-mcp": sub("scan"),
|
|
22422
|
+
go: sub("test", "build", "vet", "install", "generate"),
|
|
22423
|
+
"golangci-lint": sub("run"),
|
|
22424
|
+
gotestsum: "any",
|
|
22425
|
+
staticcheck: "any",
|
|
22426
|
+
cargo: sub("build", "b", "test", "t", "check", "c", "clippy", "bench", "nextest", "doc", "install", "tarpaulin", "llvm-cov"),
|
|
22427
|
+
mvn: "any",
|
|
22428
|
+
mvnw: "any",
|
|
22429
|
+
gradle: "any",
|
|
22430
|
+
gradlew: "any",
|
|
22431
|
+
ant: "any",
|
|
22432
|
+
sbt: "any",
|
|
22433
|
+
bazel: sub("build", "test", "run", "coverage"),
|
|
22434
|
+
bazelisk: sub("build", "test", "run", "coverage"),
|
|
22435
|
+
buck2: sub("build", "test", "run"),
|
|
22436
|
+
dotnet: sub("build", "test", "publish", "restore", "pack", "msbuild"),
|
|
22437
|
+
msbuild: "any",
|
|
22438
|
+
pytest: "any",
|
|
22439
|
+
"py.test": "any",
|
|
22440
|
+
tox: "any",
|
|
22441
|
+
nox: "any",
|
|
22442
|
+
mypy: "any",
|
|
22443
|
+
pyright: "any",
|
|
22444
|
+
pylint: "any",
|
|
22445
|
+
coverage: sub("run"),
|
|
22446
|
+
maturin: sub("build", "develop"),
|
|
22447
|
+
pip: sub("install", "wheel"),
|
|
22448
|
+
pip3: sub("install", "wheel"),
|
|
22449
|
+
rspec: "any",
|
|
22450
|
+
rails: sub("test", "assets:precompile"),
|
|
22451
|
+
phpunit: "any",
|
|
22452
|
+
pest: "any",
|
|
22453
|
+
phpstan: "any",
|
|
22454
|
+
psalm: "any",
|
|
22455
|
+
composer: sub("install", "update", "require", "test"),
|
|
22456
|
+
mix: sub("test", "compile", "deps.get", "dialyzer", "credo"),
|
|
22457
|
+
swift: sub("build", "test"),
|
|
22458
|
+
xcodebuild: "any",
|
|
22459
|
+
flutter: sub("build", "test"),
|
|
22460
|
+
dart: sub("test", "compile"),
|
|
22461
|
+
pod: sub("install"),
|
|
22462
|
+
cmake: sub("--build"),
|
|
22463
|
+
ninja: "any",
|
|
22464
|
+
meson: sub("compile", "test"),
|
|
22465
|
+
ctest: "any",
|
|
22466
|
+
scons: "any",
|
|
22467
|
+
"docker-compose": sub("build"),
|
|
22468
|
+
buildah: sub("bud", "build"),
|
|
22469
|
+
earthly: "any"
|
|
22470
|
+
};
|
|
22471
|
+
var PM_VALUES = new Set([
|
|
22472
|
+
"--prefix",
|
|
22473
|
+
"-w",
|
|
22474
|
+
"--workspace",
|
|
22475
|
+
"-C",
|
|
22476
|
+
"--dir",
|
|
22477
|
+
"--filter",
|
|
22478
|
+
"-F",
|
|
22479
|
+
"--cwd",
|
|
22480
|
+
"-p",
|
|
22481
|
+
"--package",
|
|
22482
|
+
"--cache",
|
|
22483
|
+
"--registry"
|
|
22484
|
+
]);
|
|
22485
|
+
function positional2(words, from, values = PM_VALUES) {
|
|
22486
|
+
let index = from;
|
|
22487
|
+
while (index < words.length && words[index].startsWith("-")) {
|
|
22488
|
+
if (words[index] === "--")
|
|
22489
|
+
return index + 1;
|
|
22490
|
+
index += values.has(words[index]) ? 2 : 1;
|
|
22491
|
+
}
|
|
22492
|
+
return index;
|
|
22493
|
+
}
|
|
22494
|
+
var baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, "");
|
|
22495
|
+
var WORKIT = /^(?:workit|@brainervirus\/workit-cli(?:@.*)?)$/u;
|
|
22496
|
+
var PYTHONS = /^(?:python[\d.]*|py)$/u;
|
|
22497
|
+
var NPM_INSTALL = new Set([
|
|
22498
|
+
"ci",
|
|
22499
|
+
"clean-install",
|
|
22500
|
+
"cit",
|
|
22501
|
+
"install-test",
|
|
22502
|
+
"it",
|
|
22503
|
+
"install",
|
|
22504
|
+
"i",
|
|
22505
|
+
"in",
|
|
22506
|
+
"add",
|
|
22507
|
+
"rebuild",
|
|
22508
|
+
"update",
|
|
22509
|
+
"up"
|
|
22510
|
+
]);
|
|
22511
|
+
var NPM_RUN = new Set(["run", "run-script", "rum", "urn"]);
|
|
22512
|
+
var NPM_TEST = new Set(["test", "t", "tst"]);
|
|
22513
|
+
var EXEC = new Set(["exec", "x", "dlx"]);
|
|
22514
|
+
function builtIn(words, depth = 0) {
|
|
22515
|
+
if (depth > 6 || !words.length)
|
|
22516
|
+
return null;
|
|
22517
|
+
const name = baseName2(words[0]);
|
|
22518
|
+
const label = () => words.slice(0, 3).join(" ");
|
|
22519
|
+
const inner = (from) => builtIn(words.slice(from), depth + 1);
|
|
22520
|
+
if (WORKIT.test(name))
|
|
22521
|
+
return null;
|
|
22522
|
+
if (INFO_FLAGS.has(words[1] ?? "") && words.length === 2)
|
|
22523
|
+
return null;
|
|
22524
|
+
if (words.some((word) => word === "--watch" || word.startsWith("--watch=") || word === "--watchAll"))
|
|
22525
|
+
return null;
|
|
22526
|
+
if (name === "npx" || name === "bunx" || name === "pnpx")
|
|
22527
|
+
return inner(positional2(words, 1));
|
|
22528
|
+
if (name === "corepack")
|
|
22529
|
+
return inner(1);
|
|
22530
|
+
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun" || name === "deno") {
|
|
22531
|
+
const at = positional2(words, 1);
|
|
22532
|
+
const command = words[at];
|
|
22533
|
+
if (command === undefined)
|
|
22534
|
+
return name === "yarn" ? "yarn (install)" : null;
|
|
22535
|
+
if (name === "deno") {
|
|
22536
|
+
if (["test", "bench", "check", "compile", "lint"].includes(command))
|
|
22537
|
+
return label();
|
|
22538
|
+
if (command === "task" && HEAVY_SCRIPT.test(words[at + 1] ?? ""))
|
|
22539
|
+
return label();
|
|
22540
|
+
return null;
|
|
22541
|
+
}
|
|
22542
|
+
if (NPM_INSTALL.has(command) || NPM_TEST.has(command))
|
|
22543
|
+
return label();
|
|
22544
|
+
if (name === "bun" && command === "build")
|
|
22545
|
+
return label();
|
|
22546
|
+
if (EXEC.has(command))
|
|
22547
|
+
return inner(positional2(words, at + 1));
|
|
22548
|
+
if (name === "yarn" && command === "workspace")
|
|
22549
|
+
return inner(at + 2);
|
|
22550
|
+
if (name === "yarn" && command === "workspaces")
|
|
22551
|
+
return words.slice(at + 1).some((word) => HEAVY_SCRIPT.test(word)) ? label() : null;
|
|
22552
|
+
if (NPM_RUN.has(command)) {
|
|
22553
|
+
const script = words[positional2(words, at + 1)];
|
|
22554
|
+
if (script === undefined || WATCH_SCRIPT.test(script))
|
|
22555
|
+
return null;
|
|
22556
|
+
if (HEAVY_SCRIPT.test(script))
|
|
22557
|
+
return label();
|
|
22558
|
+
return name === "bun" ? inner(positional2(words, at + 1)) : null;
|
|
22559
|
+
}
|
|
22560
|
+
if (name === "npm")
|
|
22561
|
+
return null;
|
|
22562
|
+
if (WATCH_SCRIPT.test(command))
|
|
22563
|
+
return null;
|
|
22564
|
+
if (HEAVY_SCRIPT.test(command) && !/\.[cm]?[jt]sx?$/u.test(command))
|
|
22565
|
+
return label();
|
|
22566
|
+
return inner(at);
|
|
22567
|
+
}
|
|
22568
|
+
if (PYTHONS.test(name)) {
|
|
22569
|
+
const at = words.indexOf("-m");
|
|
22570
|
+
return at > 0 && words[at + 1] ? inner(at + 1) : null;
|
|
22571
|
+
}
|
|
22572
|
+
if (["uv", "poetry", "pdm", "pipenv", "hatch", "rye"].includes(name)) {
|
|
22573
|
+
const at = positional2(words, 1);
|
|
22574
|
+
const command = words[at];
|
|
22575
|
+
if (command === "run")
|
|
22576
|
+
return inner(positional2(words, at + 1));
|
|
22577
|
+
if (command === "pip")
|
|
22578
|
+
return inner(at);
|
|
22579
|
+
return ["sync", "install", "build", "test", "add"].includes(command ?? "") ? label() : null;
|
|
22580
|
+
}
|
|
22581
|
+
if (name === "bundle") {
|
|
22582
|
+
const command = words[positional2(words, 1)];
|
|
22583
|
+
if (command === "exec")
|
|
22584
|
+
return inner(positional2(words, 2));
|
|
22585
|
+
return command === "install" || command === undefined ? label() : null;
|
|
22586
|
+
}
|
|
22587
|
+
if (name === "make" || name === "gmake") {
|
|
22588
|
+
const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
|
|
22589
|
+
if (targets.some((target) => WATCH_SCRIPT.test(target)))
|
|
22590
|
+
return null;
|
|
22591
|
+
return !targets.length || targets.some((target) => HEAVY_SCRIPT.test(target)) ? label() : null;
|
|
22592
|
+
}
|
|
22593
|
+
if (name === "rake") {
|
|
22594
|
+
const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
|
|
22595
|
+
return targets.some((target) => HEAVY_SCRIPT.test(target) || /^spec(?:$|:)/u.test(target)) ? label() : null;
|
|
22596
|
+
}
|
|
22597
|
+
if (name === "nx") {
|
|
22598
|
+
const at = positional2(words, 1);
|
|
22599
|
+
const command = words[at] ?? "";
|
|
22600
|
+
if (["run-many", "affected"].includes(command))
|
|
22601
|
+
return words.some((word) => /^(?:-t|--targets?)(?:=|$)/u.test(word)) && !words.some((word) => WATCH_SCRIPT.test(word.replace(/^--?targets?=/u, ""))) ? label() : null;
|
|
22602
|
+
if (command === "run") {
|
|
22603
|
+
const target = (words[at + 1] ?? "").split(":")[1] ?? "";
|
|
22604
|
+
return HEAVY_SCRIPT.test(target) && !WATCH_SCRIPT.test(target) ? label() : null;
|
|
22605
|
+
}
|
|
22606
|
+
return HEAVY_SCRIPT.test(command) && !WATCH_SCRIPT.test(command) ? label() : null;
|
|
22607
|
+
}
|
|
22608
|
+
if (name === "docker" || name === "podman" || name === "nerdctl") {
|
|
22609
|
+
const at = positional2(words, 1, new Set(["-H", "--host", "--context", "-c", "--config"]));
|
|
22610
|
+
const command = words[at];
|
|
22611
|
+
if (command === "build")
|
|
22612
|
+
return label();
|
|
22613
|
+
if (command === "buildx" || command === "image" || command === "builder")
|
|
22614
|
+
return words[positional2(words, at + 1)] === "build" ? label() : null;
|
|
22615
|
+
if (command === "compose") {
|
|
22616
|
+
const at2 = positional2(words, at + 1, new Set(["-f", "--file", "-p"]));
|
|
22617
|
+
const action = words[at2];
|
|
22618
|
+
if (action === "build")
|
|
22619
|
+
return label();
|
|
22620
|
+
return action === "up" && words.slice(at2 + 1).includes("--build") ? label() : null;
|
|
22621
|
+
}
|
|
22622
|
+
return null;
|
|
22623
|
+
}
|
|
22624
|
+
const rule = TOOLS[name];
|
|
22625
|
+
if (!rule)
|
|
22626
|
+
return null;
|
|
22627
|
+
if (words.slice(1).some((word) => WATCH_FLAG.test(word) || word === "watch" || word === "dev"))
|
|
22628
|
+
return null;
|
|
22629
|
+
if (rule === "any")
|
|
22630
|
+
return label();
|
|
22631
|
+
const command = words.slice(1).find((word) => !word.startsWith("-") && !word.startsWith("+") || rule.has(word));
|
|
22632
|
+
return command !== undefined && rule.has(command) ? label() : null;
|
|
22633
|
+
}
|
|
22634
|
+
var globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u");
|
|
22635
|
+
function parsePattern(pattern) {
|
|
22636
|
+
const words = pattern.trim().split(/\s+/u).filter(Boolean);
|
|
22637
|
+
return words.length ? words.map(globWord) : null;
|
|
22638
|
+
}
|
|
22639
|
+
var matches = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index]));
|
|
22640
|
+
var readJson = (file) => {
|
|
22641
|
+
try {
|
|
22642
|
+
return JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
22643
|
+
} catch {
|
|
22644
|
+
return;
|
|
22645
|
+
}
|
|
22646
|
+
};
|
|
22647
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22648
|
+
var stringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
22649
|
+
function machinePatterns(env) {
|
|
22650
|
+
const value = readJson(heavyCommandsFile(env));
|
|
22651
|
+
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
22652
|
+
}
|
|
22653
|
+
function repoPatterns(cwd) {
|
|
22654
|
+
return repoHeavyCommands(gitTop(cwd) ?? path27.resolve(cwd));
|
|
22655
|
+
}
|
|
22656
|
+
function heavyLines(lines, options) {
|
|
22657
|
+
const found = [];
|
|
22658
|
+
let patterns = null;
|
|
22659
|
+
for (const line of lines) {
|
|
22660
|
+
const reason = builtIn(line);
|
|
22661
|
+
if (reason) {
|
|
22662
|
+
found.push({ line, source: "catalog", reason });
|
|
22663
|
+
continue;
|
|
22664
|
+
}
|
|
22665
|
+
patterns ??= (options.patterns ?? [
|
|
22666
|
+
...machinePatterns(options.env ?? process.env).map((pattern) => ({
|
|
22667
|
+
pattern,
|
|
22668
|
+
from: "machine"
|
|
22669
|
+
})),
|
|
22670
|
+
...repoPatterns(options.cwd).map((pattern) => ({ pattern, from: "repo" }))
|
|
22671
|
+
]).flatMap((entry) => {
|
|
22672
|
+
const matchers = parsePattern(entry.pattern);
|
|
22673
|
+
return matchers ? [{ ...entry, matchers }] : [];
|
|
22674
|
+
});
|
|
22675
|
+
const hit = patterns.find((entry) => matches(entry.matchers, line));
|
|
22676
|
+
if (hit)
|
|
22677
|
+
found.push({
|
|
22678
|
+
line,
|
|
22679
|
+
source: { pattern: hit.pattern, from: hit.from },
|
|
22680
|
+
reason: `${hit.from} pattern "${hit.pattern}"`
|
|
22681
|
+
});
|
|
22682
|
+
}
|
|
22683
|
+
return found;
|
|
22684
|
+
}
|
|
22685
|
+
var linesOf = (command, dialect) => {
|
|
22686
|
+
try {
|
|
22687
|
+
return commandLines(command, dialect);
|
|
22688
|
+
} catch {
|
|
22689
|
+
return null;
|
|
22690
|
+
}
|
|
22691
|
+
};
|
|
22692
|
+
var quoteWord = (word) => /^[\w@%+=:,./-]+$/u.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
|
|
22693
|
+
var leaseCommand = (words) => `workit check --heavy -- ${words.map(quoteWord).join(" ")}`;
|
|
22694
|
+
function heavyPlan(command, options) {
|
|
22695
|
+
const dialect = options.dialect ?? "posix";
|
|
22696
|
+
if (dialect !== "posix" || !gitTop(options.cwd))
|
|
22697
|
+
return { kind: "none" };
|
|
22698
|
+
const lines = linesOf(command, dialect);
|
|
22699
|
+
if (!lines)
|
|
22700
|
+
return { kind: "none" };
|
|
22701
|
+
const heavy = heavyLines(lines, options);
|
|
22702
|
+
if (!heavy.length)
|
|
22703
|
+
return { kind: "none" };
|
|
22704
|
+
const simple = simpleCommand(command, dialect);
|
|
22705
|
+
const single = simple && !WORKIT.test(baseName2(simple[0])) && heavyLines([simple], options)[0];
|
|
22706
|
+
const judged = single ? [{ words: lines.flat(), text: command }] : heavy.map((match) => ({
|
|
22707
|
+
words: match.line,
|
|
22708
|
+
text: match.line.join(" ")
|
|
22709
|
+
}));
|
|
22710
|
+
if (options.rules !== null)
|
|
22711
|
+
for (const { words, text } of judged) {
|
|
22712
|
+
const verdict = wrapAllowed(options.rules, words, text, { bypass: options.bypass });
|
|
22713
|
+
if (!verdict.ok)
|
|
22714
|
+
return {
|
|
22715
|
+
kind: "passthrough",
|
|
22716
|
+
note: `workit: \`${command.trim()}\` is heavy but runs outside the heavy-check lease because a host permission rule names it (or \`workit\` is allowlisted); the host decides.`
|
|
22717
|
+
};
|
|
22718
|
+
}
|
|
22719
|
+
if (single) {
|
|
22720
|
+
const rewrite = leaseRewrite(command);
|
|
22721
|
+
return rewrite ? { kind: "rewrite", command: rewrite, reason: single.reason } : { kind: "none" };
|
|
22722
|
+
}
|
|
22723
|
+
const parts = heavy.map((match) => leaseCommand(match.line));
|
|
22724
|
+
return {
|
|
22725
|
+
kind: "split",
|
|
22726
|
+
reason: `heavy_command: \`${command.trim()}\` chains heavy work (${heavy.map((match) => match.reason).join("; ")}) with other commands, and a chain cannot run under the machine-wide heavy-check lease. Run each heavy part alone as \`workit check --heavy -- <segment>\` (here: ${parts.map((part) => `\`${part}\``).join("; ")}); run the other parts as their own commands.`,
|
|
22727
|
+
unblock: parts[0] ?? null
|
|
22728
|
+
};
|
|
22729
|
+
}
|
|
22730
|
+
function workitOnPath2(env = process.env) {
|
|
22731
|
+
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
22732
|
+
for (const dir of (env.PATH ?? "").split(path27.delimiter))
|
|
22733
|
+
for (const name of names) {
|
|
22734
|
+
if (!dir)
|
|
22735
|
+
continue;
|
|
22736
|
+
try {
|
|
22737
|
+
fs19.accessSync(path27.join(dir, name), fs19.constants.X_OK);
|
|
22738
|
+
return true;
|
|
22739
|
+
} catch {}
|
|
22740
|
+
}
|
|
22741
|
+
return false;
|
|
22742
|
+
}
|
|
22743
|
+
var ASSIGNMENTS = /^\s*((?:[A-Za-z_]\w*=(?:'[^']*'|"(?:[^"\\]|\\.)*"|[^\s'"])*\s+)*)/u;
|
|
22744
|
+
function leaseRewrite(command) {
|
|
22745
|
+
const text = command.trim();
|
|
22746
|
+
const prefix = ASSIGNMENTS.exec(text)?.[1] ?? "";
|
|
22747
|
+
const rest = text.slice(prefix.length).trim();
|
|
22748
|
+
if (!rest)
|
|
22749
|
+
return null;
|
|
22750
|
+
return `${prefix}workit check --heavy -- ${rest}`;
|
|
22751
|
+
}
|
|
22752
|
+
|
|
22753
|
+
// packages/workit-core/src/core/heavy-words.ts
|
|
22754
|
+
import fs20 from "node:fs";
|
|
22755
|
+
import os8 from "node:os";
|
|
22756
|
+
import path28 from "node:path";
|
|
22757
|
+
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(" "));
|
|
22758
|
+
var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
|
|
22759
|
+
var stateDir2 = (env) => {
|
|
22760
|
+
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
22761
|
+
return env.WORKFLOW_TOOLKIT_STATE;
|
|
22762
|
+
if (env.XDG_STATE_HOME)
|
|
22763
|
+
return path28.join(env.XDG_STATE_HOME, "workit");
|
|
22764
|
+
const home = env.HOME || os8.homedir();
|
|
22765
|
+
if (process.platform === "darwin")
|
|
22766
|
+
return path28.join(home, "Library", "Application Support", "workit");
|
|
22767
|
+
if (process.platform === "win32")
|
|
22768
|
+
return path28.join(env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
|
|
22769
|
+
return path28.join(home, ".local", "state", "workit");
|
|
22770
|
+
};
|
|
22771
|
+
var readJson2 = (file) => {
|
|
22772
|
+
try {
|
|
22773
|
+
return JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
22774
|
+
} catch {
|
|
22775
|
+
return;
|
|
22776
|
+
}
|
|
22777
|
+
};
|
|
22778
|
+
var patternWords = (cwd, env) => {
|
|
22779
|
+
const patterns = [];
|
|
22780
|
+
const machine = readJson2(path28.join(stateDir2(env), "heavy-commands.json"));
|
|
22781
|
+
if (Array.isArray(machine?.patterns))
|
|
22782
|
+
patterns.push(...machine.patterns);
|
|
22783
|
+
for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
|
|
22784
|
+
if (fs20.existsSync(path28.join(dir, ".git"))) {
|
|
22785
|
+
patterns.push(...repoHeavyCommands(dir));
|
|
22786
|
+
break;
|
|
22787
|
+
}
|
|
22788
|
+
if (path28.dirname(dir) === dir)
|
|
22789
|
+
break;
|
|
22790
|
+
}
|
|
22791
|
+
const words = new Set;
|
|
22792
|
+
for (const pattern of patterns) {
|
|
22793
|
+
if (typeof pattern !== "string")
|
|
22794
|
+
continue;
|
|
22795
|
+
const first = wordsOf(pattern)[0];
|
|
22796
|
+
if (!first || first.includes("*"))
|
|
22797
|
+
return null;
|
|
22798
|
+
words.add(first);
|
|
22799
|
+
}
|
|
22800
|
+
return words;
|
|
22801
|
+
};
|
|
22802
|
+
function maybeHeavy(command, cwd, env = process.env) {
|
|
22803
|
+
try {
|
|
22804
|
+
const words = wordsOf(command);
|
|
22805
|
+
if (words.some((word) => HEAVY_WORDS.has(word) || /^python[\d.]*$/u.test(word)))
|
|
22806
|
+
return true;
|
|
22807
|
+
const extra = patternWords(cwd, env);
|
|
22808
|
+
return extra === null || words.some((word) => extra.has(word));
|
|
22809
|
+
} catch {
|
|
22810
|
+
return true;
|
|
22811
|
+
}
|
|
22812
|
+
}
|
|
22813
|
+
|
|
22814
|
+
// packages/workit-core/src/hooks/heavy-gate.ts
|
|
22815
|
+
var rulesFor = (host, cwd, env) => {
|
|
22816
|
+
switch (host) {
|
|
22817
|
+
case "claude_code":
|
|
22818
|
+
return claudeRules(cwd, env);
|
|
22819
|
+
case "opencode":
|
|
22820
|
+
return opencodeRules(cwd, env);
|
|
22821
|
+
case "codex_cli":
|
|
22822
|
+
case "codex_desktop":
|
|
22823
|
+
return codexRules(cwd, env);
|
|
22824
|
+
case "cursor":
|
|
22825
|
+
return cursorRules(cwd, env);
|
|
22826
|
+
case "pi":
|
|
22827
|
+
return null;
|
|
22828
|
+
}
|
|
22829
|
+
};
|
|
22830
|
+
function heavyGate(cwd, command, options) {
|
|
22831
|
+
const env = options.env ?? process.env;
|
|
22832
|
+
try {
|
|
22833
|
+
if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy(command, cwd, env) || !workitOnPath2(env))
|
|
22834
|
+
return { kind: "none" };
|
|
22835
|
+
return heavyPlan(command, {
|
|
22836
|
+
cwd,
|
|
22837
|
+
env,
|
|
22838
|
+
dialect: options.dialect ?? "posix",
|
|
22839
|
+
rules: rulesFor(options.host, cwd, env),
|
|
22840
|
+
bypass: options.permissionMode === "bypassPermissions"
|
|
22841
|
+
});
|
|
22842
|
+
} catch {
|
|
22843
|
+
return { kind: "none" };
|
|
22844
|
+
}
|
|
22845
|
+
}
|
|
22846
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
22847
|
+
import { realpathSync as realpathSync6 } from "node:fs";
|
|
22848
|
+
import os10 from "node:os";
|
|
22849
|
+
import path30 from "node:path";
|
|
21978
22850
|
|
|
21979
22851
|
// packages/workit-core/src/core/live-session.ts
|
|
21980
22852
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
21981
22853
|
import { createHash as createHash14 } from "node:crypto";
|
|
21982
|
-
import
|
|
21983
|
-
import
|
|
21984
|
-
import
|
|
22854
|
+
import fs21 from "node:fs";
|
|
22855
|
+
import os9 from "node:os";
|
|
22856
|
+
import path29 from "node:path";
|
|
21985
22857
|
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
21986
22858
|
var HEARTBEAT_EVERY_MS = 30000;
|
|
21987
22859
|
var liveProbe = {
|
|
@@ -22002,9 +22874,9 @@ var processAlive = (pid) => {
|
|
|
22002
22874
|
var processInfo = (pid) => {
|
|
22003
22875
|
try {
|
|
22004
22876
|
if (liveProbe.platform === "linux") {
|
|
22005
|
-
const stat =
|
|
22877
|
+
const stat = fs21.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
22006
22878
|
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
22007
|
-
const command =
|
|
22879
|
+
const command = fs21.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
22008
22880
|
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
22009
22881
|
}
|
|
22010
22882
|
if (liveProbe.platform === "win32")
|
|
@@ -22055,35 +22927,35 @@ var liveOwner = (input, transport, env = process.env) => {
|
|
|
22055
22927
|
};
|
|
22056
22928
|
};
|
|
22057
22929
|
var checkoutOf = (target) => {
|
|
22058
|
-
let dir =
|
|
22059
|
-
while (!
|
|
22060
|
-
dir =
|
|
22061
|
-
if (
|
|
22062
|
-
dir =
|
|
22063
|
-
const parts = dir.split(
|
|
22930
|
+
let dir = path29.resolve(target);
|
|
22931
|
+
while (!fs21.existsSync(dir) && path29.dirname(dir) !== dir)
|
|
22932
|
+
dir = path29.dirname(dir);
|
|
22933
|
+
if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
|
|
22934
|
+
dir = path29.dirname(dir);
|
|
22935
|
+
const parts = dir.split(path29.sep);
|
|
22064
22936
|
const dotGit = parts.lastIndexOf(".git");
|
|
22065
22937
|
if (dotGit > 0)
|
|
22066
|
-
dir = parts.slice(0, dotGit).join(
|
|
22938
|
+
dir = parts.slice(0, dotGit).join(path29.sep) || path29.sep;
|
|
22067
22939
|
const location = resolveStore(dir);
|
|
22068
22940
|
if (location instanceof Error || !location.shared || !location.top)
|
|
22069
22941
|
return null;
|
|
22070
22942
|
let top = location.top;
|
|
22071
22943
|
try {
|
|
22072
|
-
top =
|
|
22944
|
+
top = fs21.realpathSync.native(top);
|
|
22073
22945
|
} catch {}
|
|
22074
22946
|
return top;
|
|
22075
22947
|
};
|
|
22076
|
-
var markerFile2 = (top) =>
|
|
22948
|
+
var markerFile2 = (top) => path29.join(resolveStateDir(), "live-sessions", `${createHash14("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
22077
22949
|
var checkoutKey = (top, platform = process.platform) => {
|
|
22078
|
-
let key =
|
|
22950
|
+
let key = path29.resolve(top);
|
|
22079
22951
|
try {
|
|
22080
|
-
key =
|
|
22952
|
+
key = fs21.realpathSync.native(key);
|
|
22081
22953
|
} catch {}
|
|
22082
22954
|
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
22083
22955
|
};
|
|
22084
22956
|
var readMarker = (file) => {
|
|
22085
22957
|
try {
|
|
22086
|
-
const value = JSON.parse(
|
|
22958
|
+
const value = JSON.parse(fs21.readFileSync(file, "utf8"));
|
|
22087
22959
|
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
22088
22960
|
return null;
|
|
22089
22961
|
return {
|
|
@@ -22106,7 +22978,7 @@ var isLive = (marker, deps) => {
|
|
|
22106
22978
|
const age = ageOf2(marker, deps.now ?? new Date);
|
|
22107
22979
|
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
22108
22980
|
return false;
|
|
22109
|
-
if (marker.machine !== (deps.machine ??
|
|
22981
|
+
if (marker.machine !== (deps.machine ?? os9.hostname()) || marker.pid === null)
|
|
22110
22982
|
return true;
|
|
22111
22983
|
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
22112
22984
|
return false;
|
|
@@ -22135,7 +23007,7 @@ var liveConflict = (target, owner, deps = {}) => {
|
|
|
22135
23007
|
const marker = readMarker(file);
|
|
22136
23008
|
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
22137
23009
|
return null;
|
|
22138
|
-
if (sameOwner(marker, owner, deps.machine ??
|
|
23010
|
+
if (sameOwner(marker, owner, deps.machine ?? os9.hostname()))
|
|
22139
23011
|
return null;
|
|
22140
23012
|
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
22141
23013
|
} catch {
|
|
@@ -22149,7 +23021,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
22149
23021
|
return;
|
|
22150
23022
|
const file = markerFile2(checkout);
|
|
22151
23023
|
const now = deps.now ?? new Date;
|
|
22152
|
-
const machine = deps.machine ??
|
|
23024
|
+
const machine = deps.machine ?? os9.hostname();
|
|
22153
23025
|
const current = readMarker(file);
|
|
22154
23026
|
if (current) {
|
|
22155
23027
|
const age = ageOf2(current, now);
|
|
@@ -22170,10 +23042,10 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
22170
23042
|
checkout,
|
|
22171
23043
|
heartbeatAt: now.toISOString()
|
|
22172
23044
|
};
|
|
22173
|
-
|
|
23045
|
+
fs21.mkdirSync(path29.dirname(file), { recursive: true });
|
|
22174
23046
|
const temp = `${file}.${process.pid}.tmp`;
|
|
22175
|
-
|
|
22176
|
-
|
|
23047
|
+
fs21.writeFileSync(temp, JSON.stringify(marker));
|
|
23048
|
+
fs21.renameSync(temp, file);
|
|
22177
23049
|
} catch {}
|
|
22178
23050
|
};
|
|
22179
23051
|
|
|
@@ -22182,27 +23054,42 @@ var NONE3 = { kind: "none" };
|
|
|
22182
23054
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
22183
23055
|
var canonical2 = (file) => {
|
|
22184
23056
|
let rest = "";
|
|
22185
|
-
for (let dir =
|
|
23057
|
+
for (let dir = path30.resolve(file);; dir = path30.dirname(dir)) {
|
|
22186
23058
|
try {
|
|
22187
|
-
return
|
|
23059
|
+
return path30.join(realpathSync6(dir), rest);
|
|
22188
23060
|
} catch {
|
|
22189
|
-
if (
|
|
22190
|
-
return
|
|
22191
|
-
rest = rest ?
|
|
23061
|
+
if (path30.dirname(dir) === dir)
|
|
23062
|
+
return path30.resolve(file);
|
|
23063
|
+
rest = rest ? path30.join(path30.basename(dir), rest) : path30.basename(dir);
|
|
22192
23064
|
}
|
|
22193
23065
|
}
|
|
22194
23066
|
};
|
|
22195
23067
|
var exempt = (cwd, root, file, plans) => {
|
|
22196
23068
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
22197
|
-
const relative =
|
|
22198
|
-
if (relative.startsWith("..") ||
|
|
23069
|
+
const relative = path30.relative(fold(canonical2(root)), fold(canonical2(path30.resolve(cwd, file))));
|
|
23070
|
+
if (relative.startsWith("..") || path30.isAbsolute(relative))
|
|
22199
23071
|
return true;
|
|
22200
|
-
const posix = relative.split(
|
|
23072
|
+
const posix = relative.split(path30.sep).join("/");
|
|
22201
23073
|
const segments = posix.split("/");
|
|
22202
23074
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
22203
23075
|
};
|
|
22204
23076
|
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
22205
23077
|
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
23078
|
+
var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
23079
|
+
var wrappedScript = (input) => {
|
|
23080
|
+
let words = input;
|
|
23081
|
+
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
23082
|
+
words = words.slice(1);
|
|
23083
|
+
const tail = workitCheckTail(words);
|
|
23084
|
+
if (!tail)
|
|
23085
|
+
return null;
|
|
23086
|
+
if ("script" in tail)
|
|
23087
|
+
return tail.script;
|
|
23088
|
+
const [shell, flag, script] = tail.argv;
|
|
23089
|
+
if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
|
|
23090
|
+
return script ?? null;
|
|
23091
|
+
return tail.argv.map(quoteWord2).join(" ");
|
|
23092
|
+
};
|
|
22206
23093
|
var editsOf = (input) => {
|
|
22207
23094
|
let words = input;
|
|
22208
23095
|
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
@@ -22279,10 +23166,18 @@ var editsOf = (input) => {
|
|
|
22279
23166
|
return [];
|
|
22280
23167
|
}
|
|
22281
23168
|
};
|
|
22282
|
-
var shellWrites = (command) => {
|
|
23169
|
+
var shellWrites = (command, depth = 0) => {
|
|
22283
23170
|
const targets = [];
|
|
22284
23171
|
let unknown = false;
|
|
22285
23172
|
for (const segment of segmentsOf(command)) {
|
|
23173
|
+
const inner = depth < 4 ? wrappedScript(segment.words) : null;
|
|
23174
|
+
if (inner !== null) {
|
|
23175
|
+
const nested = shellWrites(inner, depth + 1);
|
|
23176
|
+
if (nested.writes && nested.targets === null)
|
|
23177
|
+
unknown = true;
|
|
23178
|
+
else if (nested.targets)
|
|
23179
|
+
targets.push(...nested.targets);
|
|
23180
|
+
}
|
|
22286
23181
|
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
22287
23182
|
const edits = editsOf(segment.words);
|
|
22288
23183
|
if (edits === null)
|
|
@@ -22295,7 +23190,7 @@ var shellWrites = (command) => {
|
|
|
22295
23190
|
return { writes: true, targets: unknown ? null : targets };
|
|
22296
23191
|
};
|
|
22297
23192
|
var cache2 = new Map;
|
|
22298
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
23193
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path30.resolve(root, file)) ?? "-").join("\x00");
|
|
22299
23194
|
var gateFor = (cwd) => {
|
|
22300
23195
|
const store = new TaskStore(cwd);
|
|
22301
23196
|
const listed = store.listTaskIndex();
|
|
@@ -22374,7 +23269,7 @@ var gitMutationDirs = (cwd, command, dialect) => {
|
|
|
22374
23269
|
return [];
|
|
22375
23270
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
22376
23271
|
return [];
|
|
22377
|
-
return [
|
|
23272
|
+
return [path30.resolve(cwd, invocation.dir ?? ".")];
|
|
22378
23273
|
});
|
|
22379
23274
|
};
|
|
22380
23275
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
@@ -22384,15 +23279,15 @@ var cdInto = (dir, words) => {
|
|
|
22384
23279
|
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
22385
23280
|
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
22386
23281
|
return;
|
|
22387
|
-
return target === undefined ?
|
|
23282
|
+
return target === undefined ? os10.homedir() : path30.resolve(dir, target);
|
|
22388
23283
|
};
|
|
22389
|
-
var
|
|
22390
|
-
var segmentTargets = (cwd, command, dialect) => {
|
|
23284
|
+
var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
23285
|
+
var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
22391
23286
|
const targets = [];
|
|
22392
23287
|
let dir = cwd;
|
|
22393
23288
|
let stack = [];
|
|
22394
23289
|
const groups = [];
|
|
22395
|
-
const at = (file) =>
|
|
23290
|
+
const at = (file) => path30.isAbsolute(file) ? file : dir === undefined ? null : path30.resolve(dir, file);
|
|
22396
23291
|
for (const segment of segmentsOf(command, dialect)) {
|
|
22397
23292
|
if (segment.group === "open") {
|
|
22398
23293
|
groups.push({ dir, stack: [...stack] });
|
|
@@ -22420,6 +23315,9 @@ var segmentTargets = (cwd, command, dialect) => {
|
|
|
22420
23315
|
dir = stack.length ? stack.pop() : undefined;
|
|
22421
23316
|
continue;
|
|
22422
23317
|
}
|
|
23318
|
+
const inner = depth < 4 ? wrappedScript(words) : null;
|
|
23319
|
+
if (inner !== null && dir !== undefined)
|
|
23320
|
+
targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
|
|
22423
23321
|
for (const file of segment.redirects)
|
|
22424
23322
|
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
22425
23323
|
const resolved = at(file);
|
|
@@ -22429,15 +23327,15 @@ var segmentTargets = (cwd, command, dialect) => {
|
|
|
22429
23327
|
const edits = editsOf(segment.words);
|
|
22430
23328
|
if (edits === null) {
|
|
22431
23329
|
if (dir !== undefined)
|
|
22432
|
-
targets.push(
|
|
23330
|
+
targets.push(path30.join(dir, DIR_TARGET));
|
|
22433
23331
|
} else
|
|
22434
23332
|
for (const file of edits) {
|
|
22435
23333
|
const resolved = at(file);
|
|
22436
23334
|
if (resolved)
|
|
22437
23335
|
targets.push(resolved);
|
|
22438
23336
|
}
|
|
22439
|
-
if (dir !== undefined && workitInvocations(segment.words.map(
|
|
22440
|
-
targets.push(
|
|
23337
|
+
if (dir !== undefined && workitInvocations(segment.words.map(quoteWord2).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
|
|
23338
|
+
targets.push(path30.join(dir, DIR_TARGET));
|
|
22441
23339
|
}
|
|
22442
23340
|
return targets;
|
|
22443
23341
|
};
|
|
@@ -22472,7 +23370,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
|
22472
23370
|
var guarded = (targets) => {
|
|
22473
23371
|
const seen = new Set;
|
|
22474
23372
|
return targets.filter((target) => {
|
|
22475
|
-
const dir =
|
|
23373
|
+
const dir = path30.dirname(target);
|
|
22476
23374
|
if (seen.has(dir))
|
|
22477
23375
|
return false;
|
|
22478
23376
|
seen.add(dir);
|
|
@@ -22493,7 +23391,7 @@ var claimAll = (owner, targets, deps) => {
|
|
|
22493
23391
|
for (const target of guarded(targets))
|
|
22494
23392
|
claimLiveSession(target, owner, deps);
|
|
22495
23393
|
};
|
|
22496
|
-
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
23394
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path30.resolve(cwd, file)) : [path30.join(cwd, DIR_TARGET)];
|
|
22497
23395
|
function writeGate(cwd, paths, owner, deps) {
|
|
22498
23396
|
if (!owner)
|
|
22499
23397
|
return beforeWriteGate(cwd, paths);
|
|
@@ -22513,7 +23411,7 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
|
22513
23411
|
try {
|
|
22514
23412
|
targets = [
|
|
22515
23413
|
...segmentTargets(cwd, command, dialect),
|
|
22516
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
23414
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path30.join(dir, DIR_TARGET))
|
|
22517
23415
|
];
|
|
22518
23416
|
} catch {
|
|
22519
23417
|
targets = [];
|
|
@@ -22996,7 +23894,7 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
22996
23894
|
"compact.pre": { support: "native", native: 'session.hook("compaction")' },
|
|
22997
23895
|
stop: { support: "partial", native: 'event("session.idle")' }
|
|
22998
23896
|
},
|
|
22999
|
-
shellPolicy: { deny: "native", channel: "effect", failClosed: false },
|
|
23897
|
+
shellPolicy: { deny: "native", channel: "effect", failClosed: false, rewrite: "native" },
|
|
23000
23898
|
context: {
|
|
23001
23899
|
sessionStart: "none",
|
|
23002
23900
|
perTurn: "native",
|
|
@@ -23045,11 +23943,11 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
23045
23943
|
]
|
|
23046
23944
|
};
|
|
23047
23945
|
// packages/workit-opencode/src/shared/init-apply.ts
|
|
23048
|
-
import
|
|
23946
|
+
import path35 from "node:path";
|
|
23049
23947
|
|
|
23050
23948
|
// packages/workit-core/src/core/gitignore.ts
|
|
23051
23949
|
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
23052
|
-
import
|
|
23950
|
+
import path31 from "node:path";
|
|
23053
23951
|
var GITIGNORE_ENTRIES = [
|
|
23054
23952
|
"# workit: SDD working state (never commit)",
|
|
23055
23953
|
"docs/*/sdd/",
|
|
@@ -23069,7 +23967,7 @@ var GITIGNORE_ENTRIES = [
|
|
|
23069
23967
|
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
23070
23968
|
if (!confirmed)
|
|
23071
23969
|
return { ok: false, error: "confirmed: true required" };
|
|
23072
|
-
const file =
|
|
23970
|
+
const file = path31.join(workspaceRoot, ".gitignore");
|
|
23073
23971
|
const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
|
|
23074
23972
|
const existingLines = new Set(existing.split(`
|
|
23075
23973
|
`).map((l) => l.trim()).filter(Boolean));
|
|
@@ -23097,25 +23995,25 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
|
23097
23995
|
|
|
23098
23996
|
// packages/workit-core/src/core/hygiene.ts
|
|
23099
23997
|
import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
23100
|
-
import
|
|
23998
|
+
import path32 from "node:path";
|
|
23101
23999
|
var repoRoot3 = assetRoot();
|
|
23102
|
-
var templatesDir = () =>
|
|
24000
|
+
var templatesDir = () => path32.join(repoRoot3, "templates", "hygiene");
|
|
23103
24001
|
var packageJson = (root) => {
|
|
23104
|
-
if (!existsSync10(
|
|
24002
|
+
if (!existsSync10(path32.join(root, "package.json")))
|
|
23105
24003
|
return null;
|
|
23106
24004
|
try {
|
|
23107
|
-
return JSON.parse(readFileSync7(
|
|
24005
|
+
return JSON.parse(readFileSync7(path32.join(root, "package.json"), "utf8"));
|
|
23108
24006
|
} catch {
|
|
23109
24007
|
return null;
|
|
23110
24008
|
}
|
|
23111
24009
|
};
|
|
23112
24010
|
var isOpenSource = (root) => {
|
|
23113
|
-
if (existsSync10(
|
|
24011
|
+
if (existsSync10(path32.join(root, "LICENSE")))
|
|
23114
24012
|
return true;
|
|
23115
24013
|
const pkg = packageJson(root);
|
|
23116
24014
|
if (pkg && !pkg.private)
|
|
23117
24015
|
return true;
|
|
23118
|
-
return
|
|
24016
|
+
return path32.basename(root) === "workflow-toolkit";
|
|
23119
24017
|
};
|
|
23120
24018
|
var licenseHolder = (root) => {
|
|
23121
24019
|
const pkg = packageJson(root);
|
|
@@ -23138,13 +24036,13 @@ var planHygieneFiles = (root, opts = {}) => {
|
|
|
23138
24036
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
23139
24037
|
const planned = [];
|
|
23140
24038
|
for (const file of files) {
|
|
23141
|
-
if (existsSync10(
|
|
24039
|
+
if (existsSync10(path32.join(root, file)))
|
|
23142
24040
|
continue;
|
|
23143
|
-
const tpl =
|
|
24041
|
+
const tpl = path32.join(templatesDir(), file);
|
|
23144
24042
|
if (!existsSync10(tpl))
|
|
23145
24043
|
continue;
|
|
23146
|
-
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g,
|
|
23147
|
-
planned.push({ path:
|
|
24044
|
+
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path32.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
24045
|
+
planned.push({ path: path32.join(root, file), content });
|
|
23148
24046
|
}
|
|
23149
24047
|
return planned;
|
|
23150
24048
|
};
|
|
@@ -23154,19 +24052,19 @@ var ensureHygieneFiles = (root, opts) => {
|
|
|
23154
24052
|
const created = [];
|
|
23155
24053
|
for (const planned of planHygieneFiles(root, opts)) {
|
|
23156
24054
|
writeFileSync5(planned.path, planned.content, "utf8");
|
|
23157
|
-
created.push(
|
|
24055
|
+
created.push(path32.basename(planned.path));
|
|
23158
24056
|
}
|
|
23159
24057
|
return { ok: true, created };
|
|
23160
24058
|
};
|
|
23161
24059
|
|
|
23162
24060
|
// packages/workit-core/src/core/init.ts
|
|
23163
|
-
import
|
|
23164
|
-
import
|
|
24061
|
+
import fs22 from "node:fs";
|
|
24062
|
+
import path34 from "node:path";
|
|
23165
24063
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
23166
24064
|
|
|
23167
24065
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
23168
24066
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
23169
|
-
import
|
|
24067
|
+
import path33 from "node:path";
|
|
23170
24068
|
import { isDeepStrictEqual } from "node:util";
|
|
23171
24069
|
var readFileSafe = (p) => {
|
|
23172
24070
|
try {
|
|
@@ -23177,7 +24075,7 @@ var readFileSafe = (p) => {
|
|
|
23177
24075
|
};
|
|
23178
24076
|
function applyWorkspaceBranchPolicy(opts) {
|
|
23179
24077
|
const { workspace_root, env = process.env } = opts;
|
|
23180
|
-
const dir =
|
|
24078
|
+
const dir = path33.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
23181
24079
|
const {
|
|
23182
24080
|
status,
|
|
23183
24081
|
path: wsPath,
|
|
@@ -23189,7 +24087,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
23189
24087
|
if (status === "malformed" || status === "invalid")
|
|
23190
24088
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
23191
24089
|
const detection = detectBranchPolicy(workspace_root);
|
|
23192
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
24090
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path33.basename(workspace_root));
|
|
23193
24091
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
23194
24092
|
const policy = {
|
|
23195
24093
|
preset: detection.preset,
|
|
@@ -23238,7 +24136,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
23238
24136
|
ok: false,
|
|
23239
24137
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
23240
24138
|
};
|
|
23241
|
-
mkdirSync4(
|
|
24139
|
+
mkdirSync4(path33.dirname(wsPath), { recursive: true });
|
|
23242
24140
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
23243
24141
|
try {
|
|
23244
24142
|
writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
|
|
@@ -23271,9 +24169,9 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
23271
24169
|
|
|
23272
24170
|
// packages/workit-core/src/core/init.ts
|
|
23273
24171
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
23274
|
-
var
|
|
24172
|
+
var readJson3 = (p) => {
|
|
23275
24173
|
try {
|
|
23276
|
-
const parsed = JSON.parse(
|
|
24174
|
+
const parsed = JSON.parse(fs22.readFileSync(p, "utf8"));
|
|
23277
24175
|
return isConfigObject(parsed) ? parsed : null;
|
|
23278
24176
|
} catch {
|
|
23279
24177
|
return null;
|
|
@@ -23303,7 +24201,7 @@ var youtrackJsonContent = (dir) => {
|
|
|
23303
24201
|
const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
|
|
23304
24202
|
return {
|
|
23305
24203
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
|
|
23306
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
24204
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path34.join(dir, "youtrack.token"),
|
|
23307
24205
|
...meetingIssue ? {
|
|
23308
24206
|
meetingIssue,
|
|
23309
24207
|
meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
|
|
@@ -23333,19 +24231,19 @@ var vcsJsonContent = () => {
|
|
|
23333
24231
|
};
|
|
23334
24232
|
function initApplyData(action, env = process.env) {
|
|
23335
24233
|
const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
23336
|
-
|
|
24234
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
23337
24235
|
switch (action) {
|
|
23338
24236
|
case "youtrack_json": {
|
|
23339
|
-
const out =
|
|
23340
|
-
|
|
24237
|
+
const out = path34.join(dir, "youtrack.json");
|
|
24238
|
+
fs22.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
23341
24239
|
`, "utf8");
|
|
23342
24240
|
return { action, ok: true, path: out };
|
|
23343
24241
|
}
|
|
23344
24242
|
case "youtrack_token_placeholder": {
|
|
23345
|
-
const p =
|
|
24243
|
+
const p = path34.join(dir, "youtrack.token");
|
|
23346
24244
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
|
|
23347
24245
|
`, 384) === "preserved";
|
|
23348
|
-
const abs =
|
|
24246
|
+
const abs = path34.resolve(p);
|
|
23349
24247
|
return {
|
|
23350
24248
|
action,
|
|
23351
24249
|
ok: true,
|
|
@@ -23357,19 +24255,19 @@ function initApplyData(action, env = process.env) {
|
|
|
23357
24255
|
};
|
|
23358
24256
|
}
|
|
23359
24257
|
case "youtrack_scaffold": {
|
|
23360
|
-
const jsonOut =
|
|
23361
|
-
const tokenOut =
|
|
23362
|
-
|
|
24258
|
+
const jsonOut = path34.join(dir, "youtrack.json");
|
|
24259
|
+
const tokenOut = path34.join(dir, "youtrack.token");
|
|
24260
|
+
fs22.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
23363
24261
|
`, "utf8");
|
|
23364
24262
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
|
|
23365
24263
|
`, 384) === "preserved";
|
|
23366
|
-
const configPath =
|
|
23367
|
-
const tokenPath =
|
|
24264
|
+
const configPath = path34.resolve(jsonOut);
|
|
24265
|
+
const tokenPath = path34.resolve(tokenOut);
|
|
23368
24266
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
23369
24267
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
23370
24268
|
let tokenCreate = {};
|
|
23371
24269
|
try {
|
|
23372
|
-
const cfg =
|
|
24270
|
+
const cfg = readJson3(configPath) ?? {};
|
|
23373
24271
|
const base = String(cfg.baseUrl ?? "").replace(/\/+$/, "");
|
|
23374
24272
|
const meeting = String(cfg.meetingIssue ?? "");
|
|
23375
24273
|
tokenCreate = youTrackTokenCreateUrl().data;
|
|
@@ -23416,14 +24314,14 @@ function initApplyData(action, env = process.env) {
|
|
|
23416
24314
|
}
|
|
23417
24315
|
}
|
|
23418
24316
|
case "vcs_scaffold": {
|
|
23419
|
-
const jsonOut =
|
|
23420
|
-
|
|
24317
|
+
const jsonOut = path34.join(dir, "vcs.json");
|
|
24318
|
+
fs22.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
|
|
23421
24319
|
`, "utf8");
|
|
23422
|
-
const configPath =
|
|
24320
|
+
const configPath = path34.resolve(jsonOut);
|
|
23423
24321
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
23424
24322
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
23425
24323
|
try {
|
|
23426
|
-
const cfg =
|
|
24324
|
+
const cfg = readJson3(configPath) ?? {};
|
|
23427
24325
|
const rawProvider = cfg.provider;
|
|
23428
24326
|
const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider : null;
|
|
23429
24327
|
return {
|
|
@@ -23538,7 +24436,7 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
23538
24436
|
protectedNames: args.branch_policy_protected
|
|
23539
24437
|
}, current);
|
|
23540
24438
|
writeConfig(next);
|
|
23541
|
-
return output(ok({ action: "config", path:
|
|
24439
|
+
return output(ok({ action: "config", path: path35.join(configDir(), "config.json"), ...next }));
|
|
23542
24440
|
}
|
|
23543
24441
|
const env = Object.fromEntries(Object.entries({
|
|
23544
24442
|
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
@@ -23555,14 +24453,14 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
23555
24453
|
|
|
23556
24454
|
// packages/workit-opencode/src/shared/session.ts
|
|
23557
24455
|
import { realpathSync as realpathSync7 } from "node:fs";
|
|
23558
|
-
import
|
|
24456
|
+
import path36 from "node:path";
|
|
23559
24457
|
var sameWorkspace = (expected, observed) => {
|
|
23560
24458
|
if (typeof observed !== "string" || !observed)
|
|
23561
24459
|
return false;
|
|
23562
24460
|
try {
|
|
23563
24461
|
return realpathSync7(expected) === realpathSync7(observed);
|
|
23564
24462
|
} catch {
|
|
23565
|
-
return
|
|
24463
|
+
return path36.resolve(expected) === path36.resolve(observed);
|
|
23566
24464
|
}
|
|
23567
24465
|
};
|
|
23568
24466
|
|
|
@@ -23634,7 +24532,7 @@ var workitFamilyOf = (toolName) => {
|
|
|
23634
24532
|
|
|
23635
24533
|
// packages/workit-opencode/src/tools/context.ts
|
|
23636
24534
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
23637
|
-
import
|
|
24535
|
+
import path37 from "node:path";
|
|
23638
24536
|
var createContextTool = () => ({
|
|
23639
24537
|
description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
|
|
23640
24538
|
execute: async (args, context) => {
|
|
@@ -23644,7 +24542,7 @@ var createContextTool = () => ({
|
|
|
23644
24542
|
let root;
|
|
23645
24543
|
try {
|
|
23646
24544
|
const cwd = parsed.data.cwd;
|
|
23647
|
-
root = realpathSync8(cwd ?
|
|
24545
|
+
root = realpathSync8(cwd ? path37.resolve(context.directory, cwd) : context.directory);
|
|
23648
24546
|
} catch {
|
|
23649
24547
|
return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
|
|
23650
24548
|
}
|
|
@@ -23675,19 +24573,19 @@ var workerCoordinatorFor = (task, worker) => {
|
|
|
23675
24573
|
|
|
23676
24574
|
// packages/workit-core/src/fanout.ts
|
|
23677
24575
|
import { createHash as createHash16 } from "node:crypto";
|
|
23678
|
-
import
|
|
23679
|
-
import
|
|
24576
|
+
import fs24 from "node:fs";
|
|
24577
|
+
import path40 from "node:path";
|
|
23680
24578
|
|
|
23681
24579
|
// packages/workit-core/src/stack.ts
|
|
23682
24580
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
23683
24581
|
import { createHash as createHash15, randomBytes as randomBytes4 } from "node:crypto";
|
|
23684
|
-
import
|
|
23685
|
-
import
|
|
24582
|
+
import fs23 from "node:fs";
|
|
24583
|
+
import path39 from "node:path";
|
|
23686
24584
|
|
|
23687
24585
|
// packages/workit-core/src/git/ops.ts
|
|
23688
24586
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
23689
24587
|
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
|
|
23690
|
-
import
|
|
24588
|
+
import path38 from "node:path";
|
|
23691
24589
|
|
|
23692
24590
|
// packages/workit-core/src/forge/redact.ts
|
|
23693
24591
|
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_.-]*`;
|
|
@@ -23731,18 +24629,18 @@ function operationInProgress(cwd) {
|
|
|
23731
24629
|
if (!run.ok)
|
|
23732
24630
|
return null;
|
|
23733
24631
|
const found = run.stdout.split(`
|
|
23734
|
-
`).map((line) =>
|
|
24632
|
+
`).map((line) => path38.resolve(cwd, line.trim()));
|
|
23735
24633
|
const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
|
|
23736
24634
|
for (const [index, [, operation]] of markers.entries()) {
|
|
23737
24635
|
const marker = found[index];
|
|
23738
24636
|
if (!marker || !existsSync11(marker))
|
|
23739
24637
|
continue;
|
|
23740
|
-
if (index === 1 && existsSync11(
|
|
24638
|
+
if (index === 1 && existsSync11(path38.join(marker, "applying")))
|
|
23741
24639
|
return { operation: "am", commitAllowed: false, branch: null };
|
|
23742
|
-
if (index === 0 && existsSync11(
|
|
24640
|
+
if (index === 0 && existsSync11(path38.join(marker, "amend")) && !conflicted()) {
|
|
23743
24641
|
let headName = "";
|
|
23744
24642
|
try {
|
|
23745
|
-
headName = readFileSync9(
|
|
24643
|
+
headName = readFileSync9(path38.join(marker, "head-name"), "utf8").trim();
|
|
23746
24644
|
} catch {}
|
|
23747
24645
|
const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
|
|
23748
24646
|
return { operation, commitAllowed: branch !== null, branch };
|
|
@@ -23854,7 +24752,7 @@ function rebasingBranch2(dir) {
|
|
|
23854
24752
|
if (!found.ok)
|
|
23855
24753
|
continue;
|
|
23856
24754
|
try {
|
|
23857
|
-
return
|
|
24755
|
+
return fs23.readFileSync(found.stdout.trim(), "utf8").trim();
|
|
23858
24756
|
} catch {}
|
|
23859
24757
|
}
|
|
23860
24758
|
return null;
|
|
@@ -23862,14 +24760,14 @@ function rebasingBranch2(dir) {
|
|
|
23862
24760
|
function rebaseInProgress(dir) {
|
|
23863
24761
|
for (const name of ["rebase-merge", "rebase-apply"]) {
|
|
23864
24762
|
const found = gitRun3(dir, ["rev-parse", "--path-format=absolute", "--git-path", name]);
|
|
23865
|
-
if (found.ok &&
|
|
24763
|
+
if (found.ok && fs23.existsSync(found.stdout.trim()))
|
|
23866
24764
|
return true;
|
|
23867
24765
|
}
|
|
23868
24766
|
return false;
|
|
23869
24767
|
}
|
|
23870
24768
|
var scratchDir = (cwd) => {
|
|
23871
24769
|
const dir = stacksDir(cwd);
|
|
23872
|
-
return dir.ok ?
|
|
24770
|
+
return dir.ok ? path39.join(dir.data, "worktrees") : null;
|
|
23873
24771
|
};
|
|
23874
24772
|
function gitRestack(cwd, input) {
|
|
23875
24773
|
const owner = worktreeOf(cwd, input.branch);
|
|
@@ -23878,8 +24776,8 @@ function gitRestack(cwd, input) {
|
|
|
23878
24776
|
const scratch = scratchDir(cwd);
|
|
23879
24777
|
if (!scratch)
|
|
23880
24778
|
return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
|
|
23881
|
-
|
|
23882
|
-
dir =
|
|
24779
|
+
fs23.mkdirSync(scratch, { recursive: true });
|
|
24780
|
+
dir = path39.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
|
|
23883
24781
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
23884
24782
|
if (!added.ok)
|
|
23885
24783
|
return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
|
|
@@ -23923,13 +24821,14 @@ function stacksDir(cwd) {
|
|
|
23923
24821
|
const root = storeRoot(cwd);
|
|
23924
24822
|
if (!root.ok)
|
|
23925
24823
|
return stackFail(root.code, root.error, root.unblock);
|
|
23926
|
-
return { ok: true, data:
|
|
24824
|
+
return { ok: true, data: path39.join(root.value.root, "stacks") };
|
|
23927
24825
|
}
|
|
23928
24826
|
var HEARTBEAT_STALE_MS = 2 * 60000;
|
|
23929
24827
|
|
|
23930
24828
|
// packages/workit-core/src/fanout.ts
|
|
23931
24829
|
var FANOUT_VERSION = 1;
|
|
23932
24830
|
var TIERS = ["mundane", "standard", "hard"];
|
|
24831
|
+
var REASONS = ["explore", "parallel", "backstop", "high-risk-verify"];
|
|
23933
24832
|
var fanoutFail = (code, error, unblock, data) => ({
|
|
23934
24833
|
ok: false,
|
|
23935
24834
|
code,
|
|
@@ -23985,7 +24884,7 @@ function normalizePattern(raw) {
|
|
|
23985
24884
|
return { error: "has unbalanced or nested braces" };
|
|
23986
24885
|
return { pattern };
|
|
23987
24886
|
}
|
|
23988
|
-
var
|
|
24887
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23989
24888
|
function findCycle(slices) {
|
|
23990
24889
|
const byId = new Map(slices.map((slice) => [slice.id, slice]));
|
|
23991
24890
|
const state = new Map;
|
|
@@ -24021,6 +24920,7 @@ function sliceHash(slice) {
|
|
|
24021
24920
|
hash: _hash,
|
|
24022
24921
|
hashSince: _since,
|
|
24023
24922
|
tier: _tier,
|
|
24923
|
+
reason: _reason,
|
|
24024
24924
|
timebox: _timebox,
|
|
24025
24925
|
context: _context,
|
|
24026
24926
|
issues: _issues,
|
|
@@ -24032,10 +24932,10 @@ function fanoutsDir(cwd) {
|
|
|
24032
24932
|
const root = storeRoot(cwd);
|
|
24033
24933
|
if (!root.ok)
|
|
24034
24934
|
return fanoutFail(root.code, root.error, root.unblock);
|
|
24035
|
-
return { ok: true, data:
|
|
24935
|
+
return { ok: true, data: path40.join(root.value.root, "fanouts") };
|
|
24036
24936
|
}
|
|
24037
24937
|
function parseStored(raw) {
|
|
24038
|
-
if (!
|
|
24938
|
+
if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
|
|
24039
24939
|
return null;
|
|
24040
24940
|
if (!Array.isArray(raw.slices))
|
|
24041
24941
|
return null;
|
|
@@ -24052,7 +24952,7 @@ function parseStored(raw) {
|
|
|
24052
24952
|
});
|
|
24053
24953
|
const slices = [];
|
|
24054
24954
|
for (const item of raw.slices) {
|
|
24055
|
-
if (!
|
|
24955
|
+
if (!isRecord7(item) || typeof item.id !== "string" || typeof item.branch !== "string")
|
|
24056
24956
|
return null;
|
|
24057
24957
|
slices.push({
|
|
24058
24958
|
id: item.id,
|
|
@@ -24060,6 +24960,7 @@ function parseStored(raw) {
|
|
|
24060
24960
|
base: text(item.base) || raw.trunk,
|
|
24061
24961
|
worktree: text(item.worktree),
|
|
24062
24962
|
tier: TIERS.includes(item.tier) ? item.tier : "standard",
|
|
24963
|
+
reason: REASONS.includes(item.reason) ? item.reason : null,
|
|
24063
24964
|
dependsOn: list(item.dependsOn),
|
|
24064
24965
|
scope: globs(item.scope),
|
|
24065
24966
|
owns: globs(item.owns),
|
|
@@ -24099,10 +25000,10 @@ function readFanout(cwd, name) {
|
|
|
24099
25000
|
const dir = fanoutsDir(cwd);
|
|
24100
25001
|
if (!dir.ok)
|
|
24101
25002
|
return dir;
|
|
24102
|
-
const file =
|
|
25003
|
+
const file = path40.join(dir.data, stackFileName(name));
|
|
24103
25004
|
let parsed = null;
|
|
24104
25005
|
try {
|
|
24105
|
-
parsed = parseStored(JSON.parse(
|
|
25006
|
+
parsed = parseStored(JSON.parse(fs24.readFileSync(file, "utf8")));
|
|
24106
25007
|
} catch (error) {
|
|
24107
25008
|
if (error.code === "ENOENT")
|
|
24108
25009
|
return { ok: true, data: null };
|
|
@@ -24116,7 +25017,7 @@ function listFanouts(cwd) {
|
|
|
24116
25017
|
return dir;
|
|
24117
25018
|
let names = [];
|
|
24118
25019
|
try {
|
|
24119
|
-
names =
|
|
25020
|
+
names = fs24.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
|
|
24120
25021
|
} catch {
|
|
24121
25022
|
return { ok: true, data: [] };
|
|
24122
25023
|
}
|
|
@@ -24124,7 +25025,7 @@ function listFanouts(cwd) {
|
|
|
24124
25025
|
for (const entry of names) {
|
|
24125
25026
|
let parsed = null;
|
|
24126
25027
|
try {
|
|
24127
|
-
parsed = parseStored(JSON.parse(
|
|
25028
|
+
parsed = parseStored(JSON.parse(fs24.readFileSync(path40.join(dir.data, entry), "utf8")));
|
|
24128
25029
|
} catch {
|
|
24129
25030
|
parsed = null;
|
|
24130
25031
|
}
|
|
@@ -24755,6 +25656,11 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
|
|
|
24755
25656
|
const gated = shellGate(cwd, resource, owner);
|
|
24756
25657
|
if (gated.kind === "deny")
|
|
24757
25658
|
return deny(gated.reason);
|
|
25659
|
+
if (event.effect === "allow") {
|
|
25660
|
+
const plan = heavyGate(cwd, resource, { host: "opencode" });
|
|
25661
|
+
if (plan.kind === "split")
|
|
25662
|
+
return deny(plan.reason);
|
|
25663
|
+
}
|
|
24758
25664
|
}
|
|
24759
25665
|
};
|
|
24760
25666
|
|
|
@@ -24778,7 +25684,7 @@ var childTaskMissing = (store, input) => {
|
|
|
24778
25684
|
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`;
|
|
24779
25685
|
|
|
24780
25686
|
// packages/workit-opencode/src/v2/shell.ts
|
|
24781
|
-
import
|
|
25687
|
+
import path41 from "node:path";
|
|
24782
25688
|
var commandOf = (input) => {
|
|
24783
25689
|
if (input === null || typeof input !== "object")
|
|
24784
25690
|
return null;
|
|
@@ -24787,12 +25693,28 @@ var commandOf = (input) => {
|
|
|
24787
25693
|
};
|
|
24788
25694
|
var hookInput = (root, sessionID, id, shell) => ({
|
|
24789
25695
|
host: "opencode",
|
|
24790
|
-
cwd: shell.workdir ?
|
|
25696
|
+
cwd: shell.workdir ? path41.resolve(root, shell.workdir) : root,
|
|
24791
25697
|
session: { id: sessionID, agentId: null, agentType: null, parentId: null },
|
|
24792
25698
|
permissionMode: null,
|
|
24793
25699
|
transcriptPath: null,
|
|
24794
25700
|
event: { kind: "shell.pre", command: shell.command, toolUseId: id }
|
|
24795
25701
|
});
|
|
25702
|
+
var leaseRewrite2 = (input) => {
|
|
25703
|
+
if (input.event.kind !== "shell.pre")
|
|
25704
|
+
return null;
|
|
25705
|
+
const { command } = input.event;
|
|
25706
|
+
const plan = heavyGate(input.cwd, command, { host: "opencode" });
|
|
25707
|
+
if (plan.kind !== "rewrite")
|
|
25708
|
+
return null;
|
|
25709
|
+
if (shellPolicy(input.cwd, command, "posix").kind === "deny")
|
|
25710
|
+
return null;
|
|
25711
|
+
if (rawGitPre(input, command, { nudge: false }).kind === "deny")
|
|
25712
|
+
return null;
|
|
25713
|
+
const owner = liveOwner({ host: "opencode", session: { id: input.session.id, parentId: null } }, "in-process-plugin");
|
|
25714
|
+
if (shellGate(input.cwd, command, owner).kind === "deny")
|
|
25715
|
+
return null;
|
|
25716
|
+
return plan.command;
|
|
25717
|
+
};
|
|
24796
25718
|
var observeShellBefore = (root, event, workdirs) => {
|
|
24797
25719
|
try {
|
|
24798
25720
|
if (event.tool !== "shell")
|
|
@@ -24801,6 +25723,12 @@ var observeShellBefore = (root, event, workdirs) => {
|
|
|
24801
25723
|
if (!shell)
|
|
24802
25724
|
return;
|
|
24803
25725
|
const sessionID = String(event.sessionID);
|
|
25726
|
+
const original = hookInput(root, sessionID, String(event.id), shell);
|
|
25727
|
+
const heavy = leaseRewrite2(original);
|
|
25728
|
+
if (heavy) {
|
|
25729
|
+
event.input.command = heavy;
|
|
25730
|
+
shell.command = heavy;
|
|
25731
|
+
}
|
|
24804
25732
|
const input = hookInput(root, sessionID, String(event.id), shell);
|
|
24805
25733
|
workdirs.set(sessionID, input.cwd);
|
|
24806
25734
|
if (workdirs.size > 256)
|
|
@@ -24972,37 +25900,37 @@ var createStopControl = (root, port) => {
|
|
|
24972
25900
|
|
|
24973
25901
|
// packages/workit-opencode/src/v2/registry.ts
|
|
24974
25902
|
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
|
|
24975
|
-
import
|
|
25903
|
+
import path43 from "node:path";
|
|
24976
25904
|
|
|
24977
25905
|
// packages/workit-opencode/src/shared/assets.ts
|
|
24978
25906
|
import { existsSync as existsSync12, realpathSync as realpathSync9 } from "node:fs";
|
|
24979
|
-
import
|
|
25907
|
+
import path42 from "node:path";
|
|
24980
25908
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
24981
25909
|
var moduleDir = () => {
|
|
24982
25910
|
try {
|
|
24983
|
-
return realpathSync9(
|
|
25911
|
+
return realpathSync9(path42.dirname(fileURLToPath2(import.meta.url)));
|
|
24984
25912
|
} catch {
|
|
24985
|
-
return
|
|
25913
|
+
return path42.dirname(fileURLToPath2(import.meta.url));
|
|
24986
25914
|
}
|
|
24987
25915
|
};
|
|
24988
25916
|
var packageRoot2 = () => {
|
|
24989
25917
|
const dir = moduleDir();
|
|
24990
25918
|
for (const candidate of [
|
|
24991
|
-
|
|
24992
|
-
|
|
24993
|
-
|
|
25919
|
+
path42.resolve(dir, ".."),
|
|
25920
|
+
path42.resolve(dir, "..", ".."),
|
|
25921
|
+
path42.resolve(dir, "..", "..", "..")
|
|
24994
25922
|
]) {
|
|
24995
|
-
if (existsSync12(
|
|
25923
|
+
if (existsSync12(path42.join(candidate, "package.json")))
|
|
24996
25924
|
return candidate;
|
|
24997
25925
|
}
|
|
24998
|
-
return
|
|
25926
|
+
return path42.resolve(dir, "..");
|
|
24999
25927
|
};
|
|
25000
25928
|
var skillsRoot = () => {
|
|
25001
25929
|
const root = packageRoot2();
|
|
25002
|
-
const canonical =
|
|
25003
|
-
if (existsSync12(
|
|
25930
|
+
const canonical = path42.resolve(root, "..", "workit-core", "skills");
|
|
25931
|
+
if (existsSync12(path42.join(root, "src")) && existsSync12(canonical))
|
|
25004
25932
|
return canonical;
|
|
25005
|
-
return
|
|
25933
|
+
return path42.join(root, "assets", "skills");
|
|
25006
25934
|
};
|
|
25007
25935
|
|
|
25008
25936
|
// packages/workit-opencode/src/v2/registry.ts
|
|
@@ -25029,11 +25957,11 @@ var registerSkills = async (ctx) => {
|
|
|
25029
25957
|
if (!existsSync13(skillsDir))
|
|
25030
25958
|
return new Set;
|
|
25031
25959
|
const listed = await ctx.skill.list().catch(() => ({ data: [] }));
|
|
25032
|
-
const own = (skill) => typeof skill.path === "string" &&
|
|
25960
|
+
const own = (skill) => typeof skill.path === "string" && path43.resolve(skill.path).startsWith(skillsDir + path43.sep);
|
|
25033
25961
|
const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
|
|
25034
25962
|
const packaged = new Map;
|
|
25035
25963
|
for (const id of WORKIT_METHOD_SKILLS) {
|
|
25036
|
-
const file =
|
|
25964
|
+
const file = path43.join(skillsDir, id, "SKILL.md");
|
|
25037
25965
|
if (!userSkills.has(id) && existsSync13(file))
|
|
25038
25966
|
packaged.set(id, file);
|
|
25039
25967
|
}
|
|
@@ -25081,22 +26009,22 @@ var registerCommands = async (ctx, registeredSkills) => {
|
|
|
25081
26009
|
|
|
25082
26010
|
// packages/workit-opencode/src/stale-sources.ts
|
|
25083
26011
|
import { existsSync as existsSync14, readdirSync as readdirSync6 } from "node:fs";
|
|
25084
|
-
import
|
|
26012
|
+
import path44 from "node:path";
|
|
25085
26013
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25086
26014
|
var typescriptFiles = (directory) => {
|
|
25087
26015
|
if (!existsSync14(directory))
|
|
25088
26016
|
return [];
|
|
25089
26017
|
return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
25090
|
-
const file =
|
|
26018
|
+
const file = path44.join(directory, entry.name);
|
|
25091
26019
|
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
25092
26020
|
});
|
|
25093
26021
|
};
|
|
25094
26022
|
var pluginSourceFiles = [
|
|
25095
26023
|
...new Set([
|
|
25096
26024
|
fileURLToPath3(import.meta.url),
|
|
25097
|
-
...typescriptFiles(
|
|
25098
|
-
|
|
25099
|
-
...typescriptFiles(
|
|
26025
|
+
...typescriptFiles(path44.join(packageRoot2(), "src")),
|
|
26026
|
+
path44.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
26027
|
+
...typescriptFiles(path44.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
25100
26028
|
])
|
|
25101
26029
|
].filter((file) => existsSync14(file)).toSorted();
|
|
25102
26030
|
|
|
@@ -25213,8 +26141,11 @@ var setup = async (ctx) => {
|
|
|
25213
26141
|
const pendingShells = new Map;
|
|
25214
26142
|
await ctx.tool.hook("execute.before", async (event) => {
|
|
25215
26143
|
if (event.tool === "shell") {
|
|
25216
|
-
|
|
25217
|
-
return
|
|
26144
|
+
observeShellBefore(root, event, shellWorkdirs);
|
|
26145
|
+
return noteShellCall(pendingShells, {
|
|
26146
|
+
sessionID: String(event.sessionID),
|
|
26147
|
+
input: event.input
|
|
26148
|
+
});
|
|
25218
26149
|
}
|
|
25219
26150
|
if (event.tool === "skill")
|
|
25220
26151
|
return observeSkillLoad(root, event);
|