@brainervirus/workit-cli 8.33.0 → 8.35.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/dist/index.js +904 -527
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -9286,7 +9286,7 @@ var package_default;
|
|
|
9286
9286
|
var init_package = __esm(() => {
|
|
9287
9287
|
package_default = {
|
|
9288
9288
|
name: "@brainervirus/workit-cli",
|
|
9289
|
-
version: "8.
|
|
9289
|
+
version: "8.35.0",
|
|
9290
9290
|
private: false,
|
|
9291
9291
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
9292
9292
|
keywords: [
|
|
@@ -9324,13 +9324,13 @@ var init_package = __esm(() => {
|
|
|
9324
9324
|
},
|
|
9325
9325
|
dependencies: {
|
|
9326
9326
|
"@babel/parser": "7.29.9",
|
|
9327
|
-
"@brainervirus/workit-cursor": "^8.
|
|
9327
|
+
"@brainervirus/workit-cursor": "^8.35.0",
|
|
9328
9328
|
"@inkjs/ui": "2.0.0",
|
|
9329
9329
|
ink: "7.1.1",
|
|
9330
9330
|
react: "19.2.8"
|
|
9331
9331
|
},
|
|
9332
9332
|
devDependencies: {
|
|
9333
|
-
"@brainervirus/workit-core": "^8.
|
|
9333
|
+
"@brainervirus/workit-core": "^8.35.0"
|
|
9334
9334
|
},
|
|
9335
9335
|
engines: {
|
|
9336
9336
|
node: ">=24"
|
|
@@ -66607,6 +66607,35 @@ var init_heavy_words = __esm(() => {
|
|
|
66607
66607
|
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(" "));
|
|
66608
66608
|
});
|
|
66609
66609
|
|
|
66610
|
+
// packages/workit-core/src/hooks/hook-runs.ts
|
|
66611
|
+
import { mkdirSync as mkdirSync6, readdirSync as readdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
|
|
66612
|
+
import path37 from "node:path";
|
|
66613
|
+
var HOOK_RUNS_DIR = "hook-runs", readHookRuns = (dir) => {
|
|
66614
|
+
const runs = [];
|
|
66615
|
+
let names = [];
|
|
66616
|
+
try {
|
|
66617
|
+
names = readdirSync8(path37.join(dir, HOOK_RUNS_DIR));
|
|
66618
|
+
} catch {
|
|
66619
|
+
return runs;
|
|
66620
|
+
}
|
|
66621
|
+
for (const name of names) {
|
|
66622
|
+
try {
|
|
66623
|
+
const row = JSON.parse(readFileSync15(path37.join(dir, HOOK_RUNS_DIR, name), "utf8"));
|
|
66624
|
+
if (typeof row.host === "string" && typeof row.event === "string" && typeof row.at === "string" && typeof row.count === "number")
|
|
66625
|
+
runs.push({
|
|
66626
|
+
host: row.host,
|
|
66627
|
+
event: row.event,
|
|
66628
|
+
at: row.at,
|
|
66629
|
+
count: row.count
|
|
66630
|
+
});
|
|
66631
|
+
} catch {}
|
|
66632
|
+
}
|
|
66633
|
+
return runs;
|
|
66634
|
+
};
|
|
66635
|
+
var init_hook_runs = __esm(() => {
|
|
66636
|
+
init_heavy_words();
|
|
66637
|
+
});
|
|
66638
|
+
|
|
66610
66639
|
// packages/workit-core/src/hooks/delivery-verbs.ts
|
|
66611
66640
|
var WORKIT_ALLOWED_VERBS, ALLOWED;
|
|
66612
66641
|
var init_delivery_verbs = __esm(() => {
|
|
@@ -66635,14 +66664,15 @@ var init_delivery_verbs = __esm(() => {
|
|
|
66635
66664
|
import {
|
|
66636
66665
|
copyFileSync as copyFileSync4,
|
|
66637
66666
|
existsSync as existsSync15,
|
|
66638
|
-
|
|
66639
|
-
|
|
66667
|
+
readdirSync as readdirSync9,
|
|
66668
|
+
mkdirSync as mkdirSync7,
|
|
66669
|
+
readFileSync as readFileSync16,
|
|
66640
66670
|
realpathSync as realpathSync7,
|
|
66641
66671
|
rmSync as rmSync3,
|
|
66642
66672
|
statSync as statSync5
|
|
66643
66673
|
} from "node:fs";
|
|
66644
66674
|
import os11 from "node:os";
|
|
66645
|
-
import
|
|
66675
|
+
import path38 from "node:path";
|
|
66646
66676
|
function mergeClaudeSettings(settings, shell, skip = {}) {
|
|
66647
66677
|
const config = clone4(settings);
|
|
66648
66678
|
const owned = emptyOwned();
|
|
@@ -66652,7 +66682,11 @@ function mergeClaudeSettings(settings, shell, skip = {}) {
|
|
|
66652
66682
|
if (shell) {
|
|
66653
66683
|
const env = objectAt(config, ["env"], owned);
|
|
66654
66684
|
if (!env)
|
|
66655
|
-
return {
|
|
66685
|
+
return {
|
|
66686
|
+
config: settings,
|
|
66687
|
+
owned: emptyOwned(),
|
|
66688
|
+
error: "env is not an object"
|
|
66689
|
+
};
|
|
66656
66690
|
if (env.CLAUDE_CODE_SHELL === undefined) {
|
|
66657
66691
|
env.CLAUDE_CODE_SHELL = shell;
|
|
66658
66692
|
owned.env.CLAUDE_CODE_SHELL = shell;
|
|
@@ -66758,7 +66792,7 @@ function planHostPermissions(options) {
|
|
|
66758
66792
|
plan.blocked.push(`${target.file} is not a JSON object; left untouched`);
|
|
66759
66793
|
continue;
|
|
66760
66794
|
}
|
|
66761
|
-
const shellWanted = host === "claude-code" && options.shell !== false &&
|
|
66795
|
+
const shellWanted = host === "claude-code" && options.shell !== false && path38.basename(paths.env.SHELL ?? "") === "zsh";
|
|
66762
66796
|
const bash = shellWanted ? options.bash === undefined ? findBash(paths.env) : options.bash : null;
|
|
66763
66797
|
const merged = host === "claude-code" ? mergeClaudeSettings(parsed, bash, skip) : mergeCursorCliConfig(parsed, skip);
|
|
66764
66798
|
if (merged.error) {
|
|
@@ -66858,11 +66892,11 @@ function applyHostPermissions(plan, options = {}) {
|
|
|
66858
66892
|
try {
|
|
66859
66893
|
const target = change.before === null ? change.file : realpathSync7(change.file);
|
|
66860
66894
|
if (change.before !== null) {
|
|
66861
|
-
const backups =
|
|
66862
|
-
|
|
66863
|
-
copyFileSync4(target,
|
|
66895
|
+
const backups = path38.join(paths.stateDir, "host-permissions-backup");
|
|
66896
|
+
mkdirSync7(backups, { recursive: true, mode: 448 });
|
|
66897
|
+
copyFileSync4(target, path38.join(backups, `${new Date().toISOString().replaceAll(":", "-")}-${change.host}-${path38.basename(change.file)}`));
|
|
66864
66898
|
}
|
|
66865
|
-
|
|
66899
|
+
mkdirSync7(path38.dirname(target), { recursive: true });
|
|
66866
66900
|
writeFileAtomic(target, change.after);
|
|
66867
66901
|
const previous = record.hosts[change.host];
|
|
66868
66902
|
record.hosts[change.host] = {
|
|
@@ -66892,7 +66926,10 @@ function permissionHealth(host, options = {}) {
|
|
|
66892
66926
|
if (host === "codex") {
|
|
66893
66927
|
if (text === null)
|
|
66894
66928
|
return { state: "absent" };
|
|
66895
|
-
return text === codexRulesText() ? { state: "ok" } : {
|
|
66929
|
+
return text === codexRulesText() ? { state: "ok" } : {
|
|
66930
|
+
state: "drift",
|
|
66931
|
+
detail: `${target.file} differs from this Workit version's rules`
|
|
66932
|
+
};
|
|
66896
66933
|
}
|
|
66897
66934
|
if (text === null)
|
|
66898
66935
|
return { state: "absent" };
|
|
@@ -66915,7 +66952,37 @@ function permissionHealth(host, options = {}) {
|
|
|
66915
66952
|
detail: `${target.file} lacks ${missing.length} Workit entr${missing.length === 1 ? "y" : "ies"} (${missing[0]}${missing.length > 1 ? ", …" : ""})`
|
|
66916
66953
|
};
|
|
66917
66954
|
}
|
|
66918
|
-
|
|
66955
|
+
function permissionCoverage(host, commands, options = {}) {
|
|
66956
|
+
const target = permissionTarget(host, options);
|
|
66957
|
+
if (!existsSync15(target.hostDir))
|
|
66958
|
+
return { state: "no-host" };
|
|
66959
|
+
const cwd = options.cwd ?? process.cwd();
|
|
66960
|
+
let missing;
|
|
66961
|
+
if (host === "codex") {
|
|
66962
|
+
const patterns = codexAllowPatterns(path38.dirname(target.file));
|
|
66963
|
+
missing = commands.filter((command) => {
|
|
66964
|
+
const words = command.split(" ");
|
|
66965
|
+
return !patterns.some((p) => p.every((word, i) => words[i] === word));
|
|
66966
|
+
});
|
|
66967
|
+
} else if (host === "cursor") {
|
|
66968
|
+
const rules = [target.file, path38.join(cwd, ".cursor", "cli.json")].flatMap((file) => stringList(valueFrom(readRecord(file), "permissions", "allow")));
|
|
66969
|
+
missing = commands.filter((command) => !rules.some((rule) => cursorRuleMatches(rule, command)));
|
|
66970
|
+
} else {
|
|
66971
|
+
const files = [
|
|
66972
|
+
target.file,
|
|
66973
|
+
path38.join(cwd, ".claude", "settings.json"),
|
|
66974
|
+
path38.join(cwd, ".claude", "settings.local.json")
|
|
66975
|
+
].map(readRecord);
|
|
66976
|
+
const rules = files.flatMap((file) => stringList(valueFrom(file, "permissions", "allow")));
|
|
66977
|
+
missing = commands.filter((command) => !rules.some((rule) => claudeRuleMatches(rule, command)));
|
|
66978
|
+
const delivery = commands.some((command) => !LEASE_COMMANDS.includes(command));
|
|
66979
|
+
const auto = files.flatMap((file) => stringList(valueFrom(file, "autoMode", "allow")));
|
|
66980
|
+
if (delivery && !auto.some((line) => /\bworkit\b/iu.test(line)))
|
|
66981
|
+
missing.push("autoMode.allow line naming workit");
|
|
66982
|
+
}
|
|
66983
|
+
return missing.length === 0 ? { state: "ok" } : { state: "missing", file: target.file, missing };
|
|
66984
|
+
}
|
|
66985
|
+
var PERMISSION_HOSTS, CHECK_NAMES, LOCK_VERBS, LEASE_PREFIXES, CLAUDE_ALLOW, CLAUDE_ASK, AUTO_MODE_ENVIRONMENT = "Org-specific CLIs: workit — the user's delivery CLI. Its push, pr merge, release and PR-thread verbs run only under per-workspace grants and non-author verdicts the user configured in ~/.config/workit, outside the agent's reach.", AUTO_MODE_ALLOW = "Workit-gated delivery: `workit pr merge`, `workit git push --force-with-lease` on the session's own branch, `workit release` and `workit pr reply/resolve` on the session's repo are authorized by the user's standing Workit grants; not Merge Without Review, Git Destructive or External System Writes. Does NOT cover `--unverified`, `--admin` or any bypass flag.", DEFAULTS2 = "$defaults", CURSOR_ALLOW, CODEX_HEADER, codexRulesText = () => {
|
|
66919
66986
|
const rules = [...WORKIT_ALLOWED_VERBS, "--version"].map((verb) => {
|
|
66920
66987
|
const examples = verb === "git" ? `
|
|
66921
66988
|
match = [["workit", "git", "push", "--cwd", "/w"]],
|
|
@@ -67007,30 +67074,34 @@ ${[...rules, ...lease, grant].join(`
|
|
|
67007
67074
|
}, resolvePaths = (options) => {
|
|
67008
67075
|
const env = options.env ?? process.env;
|
|
67009
67076
|
const home = options.home ?? env.HOME ?? os11.homedir();
|
|
67010
|
-
return {
|
|
67077
|
+
return {
|
|
67078
|
+
home,
|
|
67079
|
+
env,
|
|
67080
|
+
stateDir: options.stateDir ?? stateDir({ ...env, HOME: home })
|
|
67081
|
+
};
|
|
67011
67082
|
}, permissionTarget = (host, options = {}) => {
|
|
67012
67083
|
const { home, env } = resolvePaths(options);
|
|
67013
67084
|
if (host === "claude-code") {
|
|
67014
67085
|
const dir = claudeConfigDir(home, env);
|
|
67015
|
-
return { file:
|
|
67086
|
+
return { file: path38.join(dir, "settings.json"), hostDir: dir };
|
|
67016
67087
|
}
|
|
67017
67088
|
if (host === "codex") {
|
|
67018
|
-
const dir = env.CODEX_HOME ||
|
|
67019
|
-
return { file:
|
|
67089
|
+
const dir = env.CODEX_HOME || path38.join(home, ".codex");
|
|
67090
|
+
return { file: path38.join(dir, "rules", "workit.rules"), hostDir: dir };
|
|
67020
67091
|
}
|
|
67021
|
-
const dir =
|
|
67022
|
-
return { file:
|
|
67092
|
+
const dir = path38.join(home, ".cursor");
|
|
67093
|
+
return { file: path38.join(dir, "cli-config.json"), hostDir: dir };
|
|
67023
67094
|
}, AGENT_SESSION_VARS, agentSessionVar = (env) => AGENT_SESSION_VARS.find((name) => (env[name] ?? "") !== "") ?? null, RECORD_FILE = "host-permissions.json", readOwnership = (options = {}) => {
|
|
67024
67095
|
const { stateDir } = resolvePaths(options);
|
|
67025
67096
|
try {
|
|
67026
|
-
const value = JSON.parse(
|
|
67097
|
+
const value = JSON.parse(readFileSync16(path38.join(stateDir, RECORD_FILE), "utf8"));
|
|
67027
67098
|
if (isRecord5(value) && isRecord5(value.hosts))
|
|
67028
67099
|
return { version: 1, hosts: value.hosts };
|
|
67029
67100
|
} catch {}
|
|
67030
67101
|
return { version: 1, hosts: {} };
|
|
67031
67102
|
}, writeOwnership = (record, stateDir) => {
|
|
67032
|
-
|
|
67033
|
-
writeFileAtomic(
|
|
67103
|
+
mkdirSync7(stateDir, { recursive: true });
|
|
67104
|
+
writeFileAtomic(path38.join(stateDir, RECORD_FILE), JSON.stringify(record, null, 2) + `
|
|
67034
67105
|
`, 384);
|
|
67035
67106
|
}, unionLists = (a, b) => {
|
|
67036
67107
|
const lists = { ...a };
|
|
@@ -67052,14 +67123,14 @@ ${[...rules, ...lease, grant].join(`
|
|
|
67052
67123
|
writeOwnership(record, paths.stateDir);
|
|
67053
67124
|
}, readText4 = (file) => {
|
|
67054
67125
|
try {
|
|
67055
|
-
return
|
|
67126
|
+
return readFileSync16(file, "utf8");
|
|
67056
67127
|
} catch {
|
|
67057
67128
|
return null;
|
|
67058
67129
|
}
|
|
67059
67130
|
}, findBash = (env) => {
|
|
67060
|
-
const dirs = ["/usr/bin", "/bin", ...(env.PATH ?? "").split(
|
|
67131
|
+
const dirs = ["/usr/bin", "/bin", ...(env.PATH ?? "").split(path38.delimiter).filter(Boolean)];
|
|
67061
67132
|
for (const dir of dirs) {
|
|
67062
|
-
const candidate =
|
|
67133
|
+
const candidate = path38.join(dir, "bash");
|
|
67063
67134
|
try {
|
|
67064
67135
|
if (statSync5(candidate).isFile())
|
|
67065
67136
|
return candidate;
|
|
@@ -67068,10 +67139,60 @@ ${[...rules, ...lease, grant].join(`
|
|
|
67068
67139
|
return null;
|
|
67069
67140
|
}, setupCommand = (hosts) => `workit setup --hosts=${hosts.join(",")}`, removeCodexRules = (file, options = {}) => {
|
|
67070
67141
|
const expected = permissionTarget("codex", options).file;
|
|
67071
|
-
if (
|
|
67142
|
+
if (path38.resolve(file) !== path38.resolve(expected) || !existsSync15(expected))
|
|
67072
67143
|
return false;
|
|
67073
67144
|
rmSync3(expected);
|
|
67074
67145
|
return true;
|
|
67146
|
+
}, GRANT_COMMANDS, LEASE_COMMANDS, grantedCommands = (kinds) => [
|
|
67147
|
+
...kinds.flatMap((kind) => GRANT_COMMANDS[kind] ?? []),
|
|
67148
|
+
...LEASE_COMMANDS
|
|
67149
|
+
], claudeRuleMatches = (rule, command) => {
|
|
67150
|
+
const body = /^Bash\((.*)\)$/u.exec(rule)?.[1];
|
|
67151
|
+
if (rule === "Bash" || body === "*" || body === "")
|
|
67152
|
+
return true;
|
|
67153
|
+
if (body === undefined)
|
|
67154
|
+
return false;
|
|
67155
|
+
if (body.endsWith(":*") && !body.slice(0, -2).includes("*")) {
|
|
67156
|
+
const prefix = body.slice(0, -2);
|
|
67157
|
+
return command === prefix || command.startsWith(`${prefix} `);
|
|
67158
|
+
}
|
|
67159
|
+
const escaped = body.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/gu, "\\$&")).join(".*");
|
|
67160
|
+
const pattern = body.endsWith(" *") ? escaped.replace(/ \.\*$/u, "(?: .*)?") : escaped;
|
|
67161
|
+
return new RegExp(`^${pattern}$`, "u").test(command);
|
|
67162
|
+
}, cursorRuleMatches = (rule, command) => {
|
|
67163
|
+
const full = /^Shell\((.*)\)$/u.exec(rule)?.[1];
|
|
67164
|
+
return full !== undefined && (command === full || command.startsWith(`${full} `));
|
|
67165
|
+
}, stringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [], readRecord = (file) => {
|
|
67166
|
+
const text = readText4(file);
|
|
67167
|
+
if (text === null)
|
|
67168
|
+
return null;
|
|
67169
|
+
try {
|
|
67170
|
+
const value = JSON.parse(text);
|
|
67171
|
+
return isRecord5(value) ? value : null;
|
|
67172
|
+
} catch {
|
|
67173
|
+
return null;
|
|
67174
|
+
}
|
|
67175
|
+
}, codexAllowPatterns = (dir) => {
|
|
67176
|
+
const patterns = [];
|
|
67177
|
+
let names = [];
|
|
67178
|
+
try {
|
|
67179
|
+
names = readdirSync9(dir).filter((name) => name.endsWith(".rules"));
|
|
67180
|
+
} catch {
|
|
67181
|
+
return patterns;
|
|
67182
|
+
}
|
|
67183
|
+
for (const name of names)
|
|
67184
|
+
for (const m of (readText4(path38.join(dir, name)) ?? "").matchAll(/pattern\s*=\s*(\[[^\]]*\])\s*,\s*decision\s*=\s*"allow"/gu))
|
|
67185
|
+
try {
|
|
67186
|
+
const words = JSON.parse(m[1]);
|
|
67187
|
+
if (Array.isArray(words))
|
|
67188
|
+
patterns.push(words.map(String));
|
|
67189
|
+
} catch {}
|
|
67190
|
+
return patterns;
|
|
67191
|
+
}, valueFrom = (root, ...keys) => {
|
|
67192
|
+
let node = root;
|
|
67193
|
+
for (const key of keys)
|
|
67194
|
+
node = isRecord5(node) ? node[key] : undefined;
|
|
67195
|
+
return node;
|
|
67075
67196
|
};
|
|
67076
67197
|
var init_host_permissions = __esm(() => {
|
|
67077
67198
|
init_heavy_words();
|
|
@@ -67083,20 +67204,21 @@ var init_host_permissions = __esm(() => {
|
|
|
67083
67204
|
"codex",
|
|
67084
67205
|
"cursor"
|
|
67085
67206
|
]);
|
|
67207
|
+
CHECK_NAMES = ["test", "lint", "typecheck", "build", "format", "docs"];
|
|
67208
|
+
LOCK_VERBS = ["status", "wait"];
|
|
67209
|
+
LEASE_PREFIXES = [
|
|
67210
|
+
...CHECK_NAMES.map((name) => ["check", name]),
|
|
67211
|
+
...LOCK_VERBS.map((verb) => ["lock", verb])
|
|
67212
|
+
];
|
|
67086
67213
|
CLAUDE_ALLOW = [
|
|
67087
67214
|
...WORKIT_ALLOWED_VERBS.map((verb) => `Bash(workit ${verb} *)`),
|
|
67215
|
+
...LEASE_PREFIXES.map((rest) => `Bash(workit ${rest.join(" ")} *)`),
|
|
67088
67216
|
"Bash(workit --version)"
|
|
67089
67217
|
];
|
|
67090
67218
|
CLAUDE_ASK = [
|
|
67091
67219
|
"Bash(workit * --unverified*)",
|
|
67092
67220
|
"Bash(workit * --overwrite-unintegrated*)"
|
|
67093
67221
|
];
|
|
67094
|
-
CHECK_NAMES = ["test", "lint", "typecheck", "build", "format", "docs"];
|
|
67095
|
-
LOCK_VERBS = ["status", "wait"];
|
|
67096
|
-
LEASE_PREFIXES = [
|
|
67097
|
-
...CHECK_NAMES.map((name) => ["check", name]),
|
|
67098
|
-
...LOCK_VERBS.map((verb) => ["lock", verb])
|
|
67099
|
-
];
|
|
67100
67222
|
CURSOR_ALLOW = [
|
|
67101
67223
|
...WORKIT_ALLOWED_VERBS.map((verb) => `Shell(workit ${verb})`),
|
|
67102
67224
|
...LEASE_PREFIXES.map((rest) => `Shell(workit ${rest.join(" ")})`),
|
|
@@ -67115,7 +67237,11 @@ var init_host_permissions = __esm(() => {
|
|
|
67115
67237
|
created: [],
|
|
67116
67238
|
env: {}
|
|
67117
67239
|
},
|
|
67118
|
-
cursor: {
|
|
67240
|
+
cursor: {
|
|
67241
|
+
lists: { "permissions.allow": [...CURSOR_ALLOW] },
|
|
67242
|
+
created: [],
|
|
67243
|
+
env: {}
|
|
67244
|
+
}
|
|
67119
67245
|
};
|
|
67120
67246
|
PRIOR_ENTRIES = {
|
|
67121
67247
|
"claude-code": {},
|
|
@@ -67142,6 +67268,12 @@ var init_host_permissions = __esm(() => {
|
|
|
67142
67268
|
"PI_SESSION_ID",
|
|
67143
67269
|
"CURSOR_AGENT"
|
|
67144
67270
|
];
|
|
67271
|
+
GRANT_COMMANDS = {
|
|
67272
|
+
push: ["workit git push"],
|
|
67273
|
+
pr: ["workit pr create"],
|
|
67274
|
+
merge: ["workit pr merge"]
|
|
67275
|
+
};
|
|
67276
|
+
LEASE_COMMANDS = LEASE_PREFIXES.map((rest) => `workit ${rest.join(" ")}`);
|
|
67145
67277
|
});
|
|
67146
67278
|
|
|
67147
67279
|
// packages/workit-cli/src/admin/doctor.ts
|
|
@@ -67150,31 +67282,34 @@ import { createHash as createHash14 } from "node:crypto";
|
|
|
67150
67282
|
import {
|
|
67151
67283
|
existsSync as existsSync16,
|
|
67152
67284
|
lstatSync as lstatSync5,
|
|
67153
|
-
mkdirSync as
|
|
67285
|
+
mkdirSync as mkdirSync8,
|
|
67154
67286
|
mkdtempSync,
|
|
67155
|
-
readdirSync as
|
|
67156
|
-
readFileSync as
|
|
67287
|
+
readdirSync as readdirSync10,
|
|
67288
|
+
readFileSync as readFileSync17,
|
|
67157
67289
|
readlinkSync as readlinkSync2,
|
|
67158
67290
|
renameSync as renameSync4,
|
|
67159
67291
|
rmSync as rmSync4,
|
|
67160
67292
|
statSync as statSync6,
|
|
67161
67293
|
unlinkSync as unlinkSync3,
|
|
67162
|
-
writeFileSync as
|
|
67294
|
+
writeFileSync as writeFileSync8
|
|
67163
67295
|
} from "node:fs";
|
|
67164
67296
|
import os12 from "node:os";
|
|
67165
|
-
import
|
|
67297
|
+
import path39 from "node:path";
|
|
67166
67298
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
67167
|
-
let dir =
|
|
67299
|
+
let dir = path39.resolve(cwd);
|
|
67168
67300
|
while (true) {
|
|
67169
|
-
if (existsSync16(
|
|
67301
|
+
if (existsSync16(path39.join(dir, "packages", "workit-core", "package.json")))
|
|
67170
67302
|
return dir;
|
|
67171
|
-
const parent =
|
|
67303
|
+
const parent = path39.dirname(dir);
|
|
67172
67304
|
if (parent === dir)
|
|
67173
67305
|
return null;
|
|
67174
67306
|
dir = parent;
|
|
67175
67307
|
}
|
|
67176
67308
|
}, listClaudeProcesses = () => {
|
|
67177
|
-
const r = spawnSync16("ps", ["-eo", "pid=,lstart=,args="], {
|
|
67309
|
+
const r = spawnSync16("ps", ["-eo", "pid=,lstart=,args="], {
|
|
67310
|
+
encoding: "utf8",
|
|
67311
|
+
timeout: 5000
|
|
67312
|
+
});
|
|
67178
67313
|
if (r.status !== 0 || typeof r.stdout !== "string")
|
|
67179
67314
|
return null;
|
|
67180
67315
|
const found = [];
|
|
@@ -67191,8 +67326,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67191
67326
|
}, resolve = (options) => {
|
|
67192
67327
|
const env = options.env ?? process.env;
|
|
67193
67328
|
const home = options.home ?? env.HOME ?? os12.homedir();
|
|
67194
|
-
const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
67195
|
-
const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ??
|
|
67329
|
+
const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path39.join(home, ".config", "workit");
|
|
67330
|
+
const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path39.join(home, ".local", "state", "workit");
|
|
67196
67331
|
const cwd = options.cwd ?? process.cwd();
|
|
67197
67332
|
const dev = options.dev?.trim() || env.WORKFLOW_TOOLKIT_DEV?.trim() || findDevFromCwd(cwd);
|
|
67198
67333
|
return {
|
|
@@ -67203,11 +67338,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67203
67338
|
cwd,
|
|
67204
67339
|
workspaceRoot: options.workspaceRoot ?? env.WORKFLOW_WORKSPACE_ROOT ?? cwd,
|
|
67205
67340
|
dev,
|
|
67206
|
-
opencodeConfig: options.opencodeConfig ??
|
|
67341
|
+
opencodeConfig: options.opencodeConfig ?? path39.join(home, ".config", "opencode", "opencode.json"),
|
|
67207
67342
|
opencodePackageCacheDir: options.opencodePackageCacheDir ?? null,
|
|
67208
|
-
cursorSettings: options.cursorSettings ??
|
|
67209
|
-
cursorMcp: options.cursorMcp ??
|
|
67210
|
-
cursorPluginDir: options.cursorPluginDir ??
|
|
67343
|
+
cursorSettings: options.cursorSettings ?? path39.join(home, ".cursor", "settings.json"),
|
|
67344
|
+
cursorMcp: options.cursorMcp ?? path39.join(home, ".cursor", "mcp.json"),
|
|
67345
|
+
cursorPluginDir: options.cursorPluginDir ?? path39.join(home, ".cursor", "plugins", "local", "workit"),
|
|
67211
67346
|
cursorHeartbeatDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
|
|
67212
67347
|
hookStateDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
|
|
67213
67348
|
codexHome: options.codexHome,
|
|
@@ -67220,14 +67355,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67220
67355
|
};
|
|
67221
67356
|
}, readJson = (p) => {
|
|
67222
67357
|
try {
|
|
67223
|
-
const parsed = JSON.parse(
|
|
67358
|
+
const parsed = JSON.parse(readFileSync17(p, "utf8"));
|
|
67224
67359
|
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
67225
67360
|
} catch {
|
|
67226
67361
|
return null;
|
|
67227
67362
|
}
|
|
67228
67363
|
}, parsesAsConfigObject = (p) => {
|
|
67229
67364
|
try {
|
|
67230
|
-
return isConfigObject(JSON.parse(
|
|
67365
|
+
return isConfigObject(JSON.parse(readFileSync17(p, "utf8")));
|
|
67231
67366
|
} catch {
|
|
67232
67367
|
return false;
|
|
67233
67368
|
}
|
|
@@ -67237,12 +67372,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67237
67372
|
...new Set(entries.filter((entry) => typeof entry === "string" && isWorkitPlugin(entry)))
|
|
67238
67373
|
];
|
|
67239
67374
|
}, commandOnPath = (name, env) => findOnPath(process.platform === "win32" ? [name, `${name}.exe`] : [name], env) !== null, pathextNames = (name, env) => process.platform === "win32" ? (env.PATHEXT ?? process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean).map((ext) => `${name}${ext.toLowerCase()}`) : [name], findOnPath = (names, env) => {
|
|
67240
|
-
const dirs = (env.PATH ?? process.env.PATH ?? "").split(
|
|
67375
|
+
const dirs = (env.PATH ?? process.env.PATH ?? "").split(path39.delimiter);
|
|
67241
67376
|
for (const dir of dirs) {
|
|
67242
67377
|
if (!dir)
|
|
67243
67378
|
continue;
|
|
67244
67379
|
for (const candidateName of names) {
|
|
67245
|
-
const candidate =
|
|
67380
|
+
const candidate = path39.join(dir, candidateName);
|
|
67246
67381
|
try {
|
|
67247
67382
|
const st = statSync6(candidate);
|
|
67248
67383
|
if (process.platform !== "win32" && (st.mode & 73) === 0)
|
|
@@ -67271,7 +67406,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67271
67406
|
if (env.BUN && existsSync16(env.BUN))
|
|
67272
67407
|
return env.BUN;
|
|
67273
67408
|
for (const candidate of [
|
|
67274
|
-
|
|
67409
|
+
path39.join(env.HOME || os12.homedir(), ".bun/bin/bun"),
|
|
67275
67410
|
"/usr/local/bin/bun",
|
|
67276
67411
|
"/usr/bin/bun"
|
|
67277
67412
|
]) {
|
|
@@ -67326,7 +67461,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67326
67461
|
detail: "installed package, not a dev checkout — version parity applies to checkouts only"
|
|
67327
67462
|
};
|
|
67328
67463
|
}
|
|
67329
|
-
const corePkg = readJson(
|
|
67464
|
+
const corePkg = readJson(path39.join(res.dev, "packages/workit-core/package.json"));
|
|
67330
67465
|
if (!corePkg) {
|
|
67331
67466
|
return {
|
|
67332
67467
|
id: "versions",
|
|
@@ -67336,7 +67471,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67336
67471
|
}
|
|
67337
67472
|
const refs = new Set;
|
|
67338
67473
|
for (const name of ["workit-opencode", "workit-cursor", "workit-cli"]) {
|
|
67339
|
-
const pkg = readJson(
|
|
67474
|
+
const pkg = readJson(path39.join(res.dev, "packages", name, "package.json"));
|
|
67340
67475
|
const dep = pkg?.dependencies?.["@brainervirus/workit-core"] ?? pkg?.devDependencies?.["@brainervirus/workit-core"];
|
|
67341
67476
|
if (typeof dep === "string")
|
|
67342
67477
|
refs.add(dep);
|
|
@@ -67352,7 +67487,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67352
67487
|
if (refs.size > 1) {
|
|
67353
67488
|
problems.push(`adapters pin different core versions: ${[...refs].join(", ")}`);
|
|
67354
67489
|
}
|
|
67355
|
-
const opencodePkg = readJson(
|
|
67490
|
+
const opencodePkg = readJson(path39.join(res.dev, "packages/workit-opencode/package.json"));
|
|
67356
67491
|
const sdk = opencodePkg?.dependencies?.["@opencode/plugin"] ?? opencodePkg?.devDependencies?.["@opencode/plugin"];
|
|
67357
67492
|
const sdkVersion = typeof sdk === "string" ? (sdk.match(/^\d+(?:\.\d+){0,2}/) ?? [])[0] : null;
|
|
67358
67493
|
if (sdkVersion && !semverAtLeast(sdkVersion, SUPPORT_MATRIX.opencode.minimum)) {
|
|
@@ -67379,14 +67514,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67379
67514
|
status: "pass",
|
|
67380
67515
|
detail: "opencode CLI not on PATH — skipping version check"
|
|
67381
67516
|
};
|
|
67382
|
-
const scratch = mkdtempSync(
|
|
67517
|
+
const scratch = mkdtempSync(path39.join(os12.tmpdir(), "workit-doctor-opencode-"));
|
|
67383
67518
|
let raw;
|
|
67384
67519
|
try {
|
|
67385
67520
|
raw = versionOf("opencode", {
|
|
67386
67521
|
...res.env,
|
|
67387
|
-
XDG_DATA_HOME:
|
|
67388
|
-
XDG_STATE_HOME:
|
|
67389
|
-
XDG_CACHE_HOME:
|
|
67522
|
+
XDG_DATA_HOME: path39.join(scratch, "data"),
|
|
67523
|
+
XDG_STATE_HOME: path39.join(scratch, "state"),
|
|
67524
|
+
XDG_CACHE_HOME: path39.join(scratch, "cache")
|
|
67390
67525
|
}, 5000);
|
|
67391
67526
|
} finally {
|
|
67392
67527
|
rmSync4(scratch, { recursive: true, force: true });
|
|
@@ -67450,7 +67585,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67450
67585
|
const tables = new Map;
|
|
67451
67586
|
let text;
|
|
67452
67587
|
try {
|
|
67453
|
-
text =
|
|
67588
|
+
text = readFileSync17(file, "utf8");
|
|
67454
67589
|
} catch {
|
|
67455
67590
|
return tables;
|
|
67456
67591
|
}
|
|
@@ -67488,19 +67623,19 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67488
67623
|
current[unquote(pair[1])] = value === "true" ? true : value === "false" ? false : unquote(value);
|
|
67489
67624
|
}
|
|
67490
67625
|
return tables;
|
|
67491
|
-
}, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME ||
|
|
67626
|
+
}, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path39.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
|
|
67492
67627
|
const codexHome = codexHomeOf(res);
|
|
67493
|
-
const configFile =
|
|
67628
|
+
const configFile = path39.join(codexHome, "config.toml");
|
|
67494
67629
|
const installs = [];
|
|
67495
67630
|
for (const [key, table] of readTomlTables(configFile)) {
|
|
67496
67631
|
const [kind, id, ...rest] = key.split("\x00");
|
|
67497
67632
|
if (kind !== "plugins" || rest.length > 0 || !id?.startsWith("workit@") || table.enabled === false)
|
|
67498
67633
|
continue;
|
|
67499
67634
|
const marketplace = id.slice("workit@".length);
|
|
67500
|
-
const dir =
|
|
67635
|
+
const dir = path39.join(codexHome, "plugins", "cache", marketplace, "workit");
|
|
67501
67636
|
let versions = [];
|
|
67502
67637
|
try {
|
|
67503
|
-
versions =
|
|
67638
|
+
versions = readdirSync10(dir).filter((v) => existsSync16(path39.join(dir, v, ".codex-plugin")));
|
|
67504
67639
|
} catch {
|
|
67505
67640
|
versions = [];
|
|
67506
67641
|
}
|
|
@@ -67508,7 +67643,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67508
67643
|
installs.push({
|
|
67509
67644
|
id,
|
|
67510
67645
|
marketplace,
|
|
67511
|
-
root: version ?
|
|
67646
|
+
root: version ? path39.join(dir, version) : null,
|
|
67512
67647
|
version,
|
|
67513
67648
|
configFile
|
|
67514
67649
|
});
|
|
@@ -67530,7 +67665,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67530
67665
|
};
|
|
67531
67666
|
return `sha256:${createHash14("sha256").update(JSON.stringify(body)).digest("hex")}`;
|
|
67532
67667
|
}, codexHookTrust = (install, tables) => {
|
|
67533
|
-
const hooks = install.root ? readJson(
|
|
67668
|
+
const hooks = install.root ? readJson(path39.join(install.root, "hooks", "hooks.json"))?.hooks : null;
|
|
67534
67669
|
if (!hooks || typeof hooks !== "object")
|
|
67535
67670
|
return null;
|
|
67536
67671
|
const result = [];
|
|
@@ -67603,25 +67738,25 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67603
67738
|
status: "pass",
|
|
67604
67739
|
detail: "no Workit Codex plugin enabled — skipping"
|
|
67605
67740
|
};
|
|
67606
|
-
const agentsDir =
|
|
67741
|
+
const agentsDir = path39.join(codexHomeOf(res), "agents");
|
|
67607
67742
|
const problems = [];
|
|
67608
67743
|
const fixes = [];
|
|
67609
67744
|
const installed = [];
|
|
67610
67745
|
for (const install of installs) {
|
|
67611
|
-
const source = install.root ?
|
|
67612
|
-
const files = source && existsSync16(source) ?
|
|
67746
|
+
const source = install.root ? path39.join(install.root, "agents") : null;
|
|
67747
|
+
const files = source && existsSync16(source) ? readdirSync10(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
|
|
67613
67748
|
if (files.length === 0) {
|
|
67614
67749
|
problems.push(`${install.id} ${install.version ?? "?"} bundles no agents`);
|
|
67615
67750
|
fixes.push(`codex plugin remove ${install.id} && codex plugin add ${install.id}`);
|
|
67616
67751
|
continue;
|
|
67617
67752
|
}
|
|
67618
|
-
const install_fix = `node ${JSON.stringify(
|
|
67619
|
-
const pluginVersion = readJson(
|
|
67753
|
+
const install_fix = `node ${JSON.stringify(path39.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
|
|
67754
|
+
const pluginVersion = readJson(path39.join(install.root, "package.json"))?.version;
|
|
67620
67755
|
const version = typeof pluginVersion === "string" ? pluginVersion.match(SEMVER)?.[0] ?? null : null;
|
|
67621
67756
|
let stale = false;
|
|
67622
67757
|
for (const file of files) {
|
|
67623
|
-
const dest =
|
|
67624
|
-
const want = stampCodexAgent(
|
|
67758
|
+
const dest = path39.join(agentsDir, file);
|
|
67759
|
+
const want = stampCodexAgent(readFileSync17(path39.join(source, file), "utf8"), version);
|
|
67625
67760
|
let link = false;
|
|
67626
67761
|
try {
|
|
67627
67762
|
link = lstatSync5(dest).isSymbolicLink();
|
|
@@ -67635,7 +67770,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67635
67770
|
}
|
|
67636
67771
|
let have = null;
|
|
67637
67772
|
try {
|
|
67638
|
-
have =
|
|
67773
|
+
have = readFileSync17(dest, "utf8");
|
|
67639
67774
|
} catch {
|
|
67640
67775
|
have = null;
|
|
67641
67776
|
}
|
|
@@ -67674,7 +67809,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67674
67809
|
const env = res.env.PI_CODING_AGENT_DIR;
|
|
67675
67810
|
if (env)
|
|
67676
67811
|
return env === "~" ? res.home : env.replace(/^~(?=\/|\\)/, res.home);
|
|
67677
|
-
return
|
|
67812
|
+
return path39.join(res.home, ".pi", "agent");
|
|
67678
67813
|
}, npmSpecName = (spec) => {
|
|
67679
67814
|
const at = spec.indexOf("@", spec.startsWith("@") ? 1 : 0);
|
|
67680
67815
|
return at > 0 ? { name: spec.slice(0, at), pinned: true } : { name: spec, pinned: false };
|
|
@@ -67682,10 +67817,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67682
67817
|
const scopes = [
|
|
67683
67818
|
{
|
|
67684
67819
|
scope: "project",
|
|
67685
|
-
settingsFile:
|
|
67686
|
-
base:
|
|
67820
|
+
settingsFile: path39.join(res.cwd, ".pi", "settings.json"),
|
|
67821
|
+
base: path39.join(res.cwd, ".pi")
|
|
67687
67822
|
},
|
|
67688
|
-
{
|
|
67823
|
+
{
|
|
67824
|
+
scope: "user",
|
|
67825
|
+
settingsFile: path39.join(agentDir, "settings.json"),
|
|
67826
|
+
base: agentDir
|
|
67827
|
+
}
|
|
67689
67828
|
];
|
|
67690
67829
|
for (const { scope, settingsFile, base } of scopes) {
|
|
67691
67830
|
const packages = readJson(settingsFile)?.packages;
|
|
@@ -67700,13 +67839,20 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67700
67839
|
const spec = npmSpecName(source.slice(4).trim());
|
|
67701
67840
|
if (spec.name !== PI_PACKAGE)
|
|
67702
67841
|
continue;
|
|
67703
|
-
const root =
|
|
67704
|
-
return {
|
|
67842
|
+
const root = path39.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
|
|
67843
|
+
return {
|
|
67844
|
+
scope,
|
|
67845
|
+
settingsFile,
|
|
67846
|
+
source,
|
|
67847
|
+
filter,
|
|
67848
|
+
root,
|
|
67849
|
+
npm: { pinned: spec.pinned }
|
|
67850
|
+
};
|
|
67705
67851
|
}
|
|
67706
67852
|
if (/^(git:|https?:|ssh:|git@)/.test(source))
|
|
67707
67853
|
continue;
|
|
67708
|
-
const local =
|
|
67709
|
-
if (readJson(
|
|
67854
|
+
const local = path39.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
|
|
67855
|
+
if (readJson(path39.join(local, "package.json"))?.name === PI_PACKAGE)
|
|
67710
67856
|
return { scope, settingsFile, source, filter, root: local, npm: null };
|
|
67711
67857
|
}
|
|
67712
67858
|
}
|
|
@@ -67717,7 +67863,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67717
67863
|
const list = patterns.filter((p) => typeof p === "string");
|
|
67718
67864
|
if (list.length === 0)
|
|
67719
67865
|
return false;
|
|
67720
|
-
const name =
|
|
67866
|
+
const name = path39.posix.basename(file);
|
|
67721
67867
|
const matches = (p) => {
|
|
67722
67868
|
const re = globToRegExp3(p);
|
|
67723
67869
|
return re.test(file) || re.test(name);
|
|
@@ -67735,19 +67881,23 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67735
67881
|
}, checkPiExtension = (res) => {
|
|
67736
67882
|
const agentDir = piAgentDirOf(res);
|
|
67737
67883
|
if (!existsSync16(agentDir) && !commandOnPath("pi", res.env))
|
|
67738
|
-
return {
|
|
67884
|
+
return {
|
|
67885
|
+
id: "pi_extension",
|
|
67886
|
+
status: "pass",
|
|
67887
|
+
detail: "Pi not installed — skipping"
|
|
67888
|
+
};
|
|
67739
67889
|
const installCmd = (scope, spec = `npm:${PI_PACKAGE}`) => `pi install ${spec}${scope === "project" ? " -l" : ""}`;
|
|
67740
67890
|
const entry = findPiEntry(res, agentDir);
|
|
67741
67891
|
if (!entry)
|
|
67742
67892
|
return {
|
|
67743
67893
|
id: "pi_extension",
|
|
67744
67894
|
status: "warn",
|
|
67745
|
-
detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${
|
|
67895
|
+
detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path39.join(agentDir, "settings.json")} or ${path39.join(res.cwd, ".pi", "settings.json")}`,
|
|
67746
67896
|
fix: installCmd("user")
|
|
67747
67897
|
};
|
|
67748
67898
|
const where = `${entry.source} (${entry.scope}, ${entry.settingsFile})`;
|
|
67749
67899
|
const rebuild = `cd ${JSON.stringify(entry.root)} && bun run build`;
|
|
67750
|
-
const pkg = readJson(
|
|
67900
|
+
const pkg = readJson(path39.join(entry.root, "package.json"));
|
|
67751
67901
|
if (!pkg)
|
|
67752
67902
|
return {
|
|
67753
67903
|
id: "pi_extension",
|
|
@@ -67756,7 +67906,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67756
67906
|
fix: entry.npm ? installCmd(entry.scope, entry.source) : `restore ${entry.root}, then ${rebuild}`
|
|
67757
67907
|
};
|
|
67758
67908
|
const declared = Array.isArray(pkg.pi?.extensions) ? pkg.pi.extensions.some((e) => typeof e === "string" && e.replace(/^\.\//, "") === PI_EXTENSION) : false;
|
|
67759
|
-
if (!declared || !existsSync16(
|
|
67909
|
+
if (!declared || !existsSync16(path39.join(entry.root, PI_EXTENSION)))
|
|
67760
67910
|
return {
|
|
67761
67911
|
id: "pi_extension",
|
|
67762
67912
|
status: "warn",
|
|
@@ -67809,21 +67959,21 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67809
67959
|
detail: `Workit Pi extension ${version ?? "?"} from ${where} loads ${PI_EXTENSION}`
|
|
67810
67960
|
};
|
|
67811
67961
|
}, assetPathsFor = (host, dev) => {
|
|
67812
|
-
const pkg =
|
|
67962
|
+
const pkg = path39.join(dev, "packages", `workit-${host}`);
|
|
67813
67963
|
switch (host) {
|
|
67814
67964
|
case "opencode": {
|
|
67815
|
-
const packaged =
|
|
67816
|
-
const root = existsSync16(packaged) ? packaged :
|
|
67817
|
-
return WORKIT_METHOD_SKILLS.map((skill) =>
|
|
67965
|
+
const packaged = path39.join(pkg, "assets", "skills");
|
|
67966
|
+
const root = existsSync16(packaged) ? packaged : path39.join(dev, "packages", "workit-core", "skills");
|
|
67967
|
+
return WORKIT_METHOD_SKILLS.map((skill) => path39.join(root, skill, "SKILL.md"));
|
|
67818
67968
|
}
|
|
67819
67969
|
case "cursor":
|
|
67820
67970
|
return [
|
|
67821
|
-
|
|
67822
|
-
|
|
67823
|
-
|
|
67971
|
+
path39.join(pkg, "assets", "templates", "workit-contract.md"),
|
|
67972
|
+
path39.join(pkg, "mcp.json"),
|
|
67973
|
+
path39.join(pkg, ".cursor-plugin")
|
|
67824
67974
|
];
|
|
67825
67975
|
case "cli":
|
|
67826
|
-
return [
|
|
67976
|
+
return [path39.join(pkg, "assets", "templates", "spec-template.md")];
|
|
67827
67977
|
}
|
|
67828
67978
|
}, hostsFor = (host) => host === "cli" ? ["opencode", "cursor", "cli"] : [host], checkAssets = (res) => {
|
|
67829
67979
|
const dev = res.dev;
|
|
@@ -67854,22 +68004,22 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67854
68004
|
fix: "Reinstall or rebuild the workit package (missing assets under packages/workit-<host>)"
|
|
67855
68005
|
};
|
|
67856
68006
|
}, launcherSlotsFor = (host, dev) => {
|
|
67857
|
-
const pkg =
|
|
68007
|
+
const pkg = path39.join(dev, "packages", `workit-${host}`);
|
|
67858
68008
|
switch (host) {
|
|
67859
68009
|
case "opencode":
|
|
67860
|
-
return [[
|
|
68010
|
+
return [[path39.join(pkg, "src", "plugin.ts"), path39.join(pkg, "dist", "plugin.js")]];
|
|
67861
68011
|
case "cursor":
|
|
67862
68012
|
return [
|
|
67863
|
-
[
|
|
67864
|
-
[
|
|
68013
|
+
[path39.join(pkg, "dist", "mcp-server.js")],
|
|
68014
|
+
[path39.join(pkg, "dist", "cursor-session-start.js")]
|
|
67865
68015
|
];
|
|
67866
68016
|
case "cli":
|
|
67867
|
-
return [[
|
|
68017
|
+
return [[path39.join(pkg, "src", "index.tsx"), path39.join(pkg, "dist", "index.js")]];
|
|
67868
68018
|
}
|
|
67869
68019
|
}, validNodeEntry = (entry, runtime, env) => {
|
|
67870
68020
|
try {
|
|
67871
68021
|
const stat = statSync6(entry);
|
|
67872
|
-
if (!stat.isFile() || stat.size === 0 || !
|
|
68022
|
+
if (!stat.isFile() || stat.size === 0 || !readFileSync17(entry, "utf8").startsWith(`#!/usr/bin/env node
|
|
67873
68023
|
`)) {
|
|
67874
68024
|
return false;
|
|
67875
68025
|
}
|
|
@@ -67891,7 +68041,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67891
68041
|
if (typeof command !== "string" || !Array.isArray(args) || typeof args[0] !== "string") {
|
|
67892
68042
|
return "invalid";
|
|
67893
68043
|
}
|
|
67894
|
-
const executable =
|
|
68044
|
+
const executable = path39.basename(command).toLowerCase();
|
|
67895
68045
|
const canonical = cursorMcpServerEntry("").args;
|
|
67896
68046
|
if (executable === "npx" || executable === "npx.exe" || executable === "npx.cmd") {
|
|
67897
68047
|
if (args.length !== canonical.length || args.some((a, i) => a !== canonical[i]))
|
|
@@ -67903,10 +68053,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67903
68053
|
return {
|
|
67904
68054
|
kind: "node",
|
|
67905
68055
|
runtime: command,
|
|
67906
|
-
entry:
|
|
68056
|
+
entry: path39.isAbsolute(args[0]) ? args[0] : path39.resolve(path39.dirname(res.cursorMcp), args[0])
|
|
67907
68057
|
};
|
|
67908
68058
|
}, canonicalCursorHook, isCanonicalCursorHook = (command, res) => command === canonicalCursorHook || command === cursorHooksEntry(res.cursorPluginDir).command, registeredCursorHook = (res) => {
|
|
67909
|
-
const hooksFile =
|
|
68059
|
+
const hooksFile = path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
|
|
67910
68060
|
if (!existsSync16(hooksFile))
|
|
67911
68061
|
return "invalid";
|
|
67912
68062
|
const config = readJson(hooksFile);
|
|
@@ -67924,9 +68074,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67924
68074
|
if (!m)
|
|
67925
68075
|
return false;
|
|
67926
68076
|
const entry = m[1].trim();
|
|
67927
|
-
if (!
|
|
68077
|
+
if (!path39.isAbsolute(entry))
|
|
67928
68078
|
return false;
|
|
67929
|
-
if (
|
|
68079
|
+
if (path39.resolve(entry) !== path39.resolve(path39.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
|
|
67930
68080
|
return false;
|
|
67931
68081
|
}
|
|
67932
68082
|
return validNodeEntry(entry, "node", res.env);
|
|
@@ -67935,7 +68085,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67935
68085
|
const hosts = hostsFor(res.host);
|
|
67936
68086
|
const registered = hosts.includes("cursor") ? registeredCursorLauncher(res) : null;
|
|
67937
68087
|
const runtime = registered && registered !== "invalid" && registered.kind === "node" ? registered.runtime : "node";
|
|
67938
|
-
const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) =>
|
|
68088
|
+
const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) => path39.join(res.cursorPluginDir, rel)).filter((p) => !validNodeEntry(p, runtime, res.env)).map((p) => `cursor: ${p}`) : [];
|
|
67939
68089
|
if (hosts.includes("cursor")) {
|
|
67940
68090
|
if (registered === "invalid") {
|
|
67941
68091
|
missing.push(`cursor: canonical workit MCP launcher in ${res.cursorMcp}`);
|
|
@@ -67944,9 +68094,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67944
68094
|
}
|
|
67945
68095
|
const hook = registeredCursorHook(res);
|
|
67946
68096
|
if (hook === "invalid") {
|
|
67947
|
-
missing.push(`cursor: canonical session-start hook in ${
|
|
68097
|
+
missing.push(`cursor: canonical session-start hook in ${path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
|
|
67948
68098
|
} else if (hook !== null && !isCanonicalCursorHook(hook, res) && !validLocalDistHook(hook, res)) {
|
|
67949
|
-
missing.push(`cursor: canonical session-start hook in ${
|
|
68099
|
+
missing.push(`cursor: canonical session-start hook in ${path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
|
|
67950
68100
|
}
|
|
67951
68101
|
}
|
|
67952
68102
|
if (dev) {
|
|
@@ -67982,10 +68132,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
67982
68132
|
const normal = entry.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
67983
68133
|
if (!normal.endsWith("local/workit") && !normal.endsWith("local/workflow-toolkit"))
|
|
67984
68134
|
continue;
|
|
67985
|
-
const dir = entry.startsWith("~") ?
|
|
68135
|
+
const dir = entry.startsWith("~") ? path39.join(res.home, entry.slice(1)) : entry;
|
|
67986
68136
|
let state = null;
|
|
67987
68137
|
try {
|
|
67988
|
-
state =
|
|
68138
|
+
state = readdirSync10(dir).length === 0 ? "empty" : null;
|
|
67989
68139
|
} catch {
|
|
67990
68140
|
state = "missing";
|
|
67991
68141
|
}
|
|
@@ -68007,17 +68157,26 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68007
68157
|
status: "pass",
|
|
68008
68158
|
detail: "no registered Workit host plugin is missing its files"
|
|
68009
68159
|
};
|
|
68010
|
-
return {
|
|
68160
|
+
return {
|
|
68161
|
+
id: "plugin_files",
|
|
68162
|
+
status: "warn",
|
|
68163
|
+
detail: problems.join("; "),
|
|
68164
|
+
fix: fixes.join("; ")
|
|
68165
|
+
};
|
|
68011
68166
|
}, CURSOR_HOOK_FIX = "Run `workit init` and select Cursor to install the bundled hook", checkCursorHook = (res) => {
|
|
68012
68167
|
if (!hostsFor(res.host).includes("cursor"))
|
|
68013
|
-
return {
|
|
68168
|
+
return {
|
|
68169
|
+
id: "cursor_hook",
|
|
68170
|
+
status: "pass",
|
|
68171
|
+
detail: "cursor hook not inspected on this host"
|
|
68172
|
+
};
|
|
68014
68173
|
if (!existsSync16(res.cursorPluginDir))
|
|
68015
68174
|
return {
|
|
68016
68175
|
id: "cursor_hook",
|
|
68017
68176
|
status: "pass",
|
|
68018
68177
|
detail: "no Cursor plugin install — hook launcher not inspected"
|
|
68019
68178
|
};
|
|
68020
|
-
const launcher =
|
|
68179
|
+
const launcher = path39.join(res.cursorPluginDir, "hooks", "launch.mjs");
|
|
68021
68180
|
if (!existsSync16(launcher))
|
|
68022
68181
|
return {
|
|
68023
68182
|
id: "cursor_hook",
|
|
@@ -68025,7 +68184,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68025
68184
|
detail: `cursor hook launcher mode: missing — ${launcher} is absent (an install from before the pinned launcher)`,
|
|
68026
68185
|
fix: CURSOR_HOOK_FIX
|
|
68027
68186
|
};
|
|
68028
|
-
const hooksFile =
|
|
68187
|
+
const hooksFile = path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
|
|
68029
68188
|
const registered = readJson(hooksFile);
|
|
68030
68189
|
const sessionCommand = registered?.hooks?.sessionStart?.[0]?.command;
|
|
68031
68190
|
const offLauncher = [
|
|
@@ -68084,7 +68243,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68084
68243
|
detail: `${label}; probe ${ms} ms; ${silent}`,
|
|
68085
68244
|
fix: "Open a Cursor agent chat, then re-run `workit doctor`; if this persists, re-run `workit init` with Cursor (it writes absolute hook paths)"
|
|
68086
68245
|
};
|
|
68087
|
-
return {
|
|
68246
|
+
return {
|
|
68247
|
+
id: "cursor_hook",
|
|
68248
|
+
status: "pass",
|
|
68249
|
+
detail: `${label}; probe ${ms} ms`
|
|
68250
|
+
};
|
|
68088
68251
|
}, SILENT_HOOK_GRACE_MS, mtimeMs = (file) => {
|
|
68089
68252
|
try {
|
|
68090
68253
|
return statSync6(file).mtimeMs;
|
|
@@ -68092,10 +68255,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68092
68255
|
return null;
|
|
68093
68256
|
}
|
|
68094
68257
|
}, silentCursorHooks = (res) => {
|
|
68095
|
-
const session = mtimeMs(
|
|
68258
|
+
const session = mtimeMs(path39.join(res.cursorHeartbeatDir, "cursor-session-last-start"));
|
|
68096
68259
|
if (session === null || Date.now() - session < SILENT_HOOK_GRACE_MS)
|
|
68097
68260
|
return null;
|
|
68098
|
-
const hook = mtimeMs(
|
|
68261
|
+
const hook = mtimeMs(path39.join(res.cursorHeartbeatDir, "cursor-hook-last-run"));
|
|
68099
68262
|
if (hook !== null && hook >= session)
|
|
68100
68263
|
return null;
|
|
68101
68264
|
return `Cursor started the Workit MCP server at ${new Date(session).toISOString()} but no Workit hook has run since${hook === null ? " (none ever)" : ""} — if the agent ran in that window, Cursor is not running the plugin hooks and Workit enforcement is off`;
|
|
@@ -68191,7 +68354,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68191
68354
|
}
|
|
68192
68355
|
return { id: "stale_pin", status: "pass", detail: "opencode pin resolves" };
|
|
68193
68356
|
}, pluginMcpSelectors = (res) => {
|
|
68194
|
-
const p =
|
|
68357
|
+
const p = path39.join(res.cursorPluginDir, "mcp.json");
|
|
68195
68358
|
if (!existsSync16(p))
|
|
68196
68359
|
return null;
|
|
68197
68360
|
const cfg = readJson(p);
|
|
@@ -68225,19 +68388,19 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68225
68388
|
return null;
|
|
68226
68389
|
}
|
|
68227
68390
|
}, installedPluginVersion = (res) => {
|
|
68228
|
-
const pkg = readJson(
|
|
68391
|
+
const pkg = readJson(path39.join(res.cursorPluginDir, "package.json"));
|
|
68229
68392
|
return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
|
|
68230
68393
|
}, installedOpenCodeCache = (res) => {
|
|
68231
68394
|
if (res.opencodePackageCacheDir !== null) {
|
|
68232
|
-
const pkg = readJson(
|
|
68395
|
+
const pkg = readJson(path39.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
|
|
68233
68396
|
return typeof pkg?.version === "string" && pkg.version ? { version: pkg.version, dir: res.opencodePackageCacheDir } : null;
|
|
68234
68397
|
}
|
|
68235
68398
|
const found = openCodeInstalledPlugin(res.home, res.env);
|
|
68236
68399
|
if (!found)
|
|
68237
68400
|
return null;
|
|
68238
|
-
let dir =
|
|
68239
|
-
if (
|
|
68240
|
-
dir =
|
|
68401
|
+
let dir = path39.dirname(path39.dirname(path39.dirname(found.root)));
|
|
68402
|
+
if (path39.basename(dir) !== "workit-opencode@latest")
|
|
68403
|
+
dir = path39.dirname(dir);
|
|
68241
68404
|
return { version: found.version, dir };
|
|
68242
68405
|
}, checkOpenCodePackageCache = (res) => {
|
|
68243
68406
|
if (!existsSync16(res.opencodeConfig)) {
|
|
@@ -68341,7 +68504,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68341
68504
|
fix: "Re-run install-cursor-plugin.sh — it rewrites the workit MCP entry to the canonical @latest selector"
|
|
68342
68505
|
};
|
|
68343
68506
|
}
|
|
68344
|
-
const hooksFile =
|
|
68507
|
+
const hooksFile = path39.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
|
|
68345
68508
|
const hookCmd = readJson(hooksFile)?.hooks?.sessionStart?.[0]?.command ?? null;
|
|
68346
68509
|
const canonicalHook = canonicalCursorHook;
|
|
68347
68510
|
const staleHook = hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && !hookCmd.startsWith("node ") && hookCmd.includes("@brainervirus/workit-cursor@");
|
|
@@ -68363,7 +68526,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68363
68526
|
};
|
|
68364
68527
|
}
|
|
68365
68528
|
const installed = installedPluginVersion(res);
|
|
68366
|
-
const source = res.dev ? readJson(
|
|
68529
|
+
const source = res.dev ? readJson(path39.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path39.join(packageRoot(), "package.json"))?.version ?? null;
|
|
68367
68530
|
const localDist = mcpSelectors === null && hookCmd !== null && !isCanonicalCursorHook(hookCmd, res) && hookCmd.startsWith("node ");
|
|
68368
68531
|
if (localDist && installed !== null && source !== null && !semverAtLeast(installed, source)) {
|
|
68369
68532
|
return {
|
|
@@ -68375,7 +68538,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68375
68538
|
}
|
|
68376
68539
|
if (localDist && installed !== null && source !== null && res.dev) {
|
|
68377
68540
|
const entry = /^node\s+(.+)$/.exec((hookCmd ?? "").trim())?.[1]?.trim() ?? "";
|
|
68378
|
-
const sourceBundle = entry &&
|
|
68541
|
+
const sourceBundle = entry && path39.isAbsolute(entry) ? path39.join(res.dev, "packages", "workit-cursor", "dist", path39.basename(entry)) : "";
|
|
68379
68542
|
if (entry && sourceBundle) {
|
|
68380
68543
|
const installedHash = bundleHashOfFile(entry);
|
|
68381
68544
|
const sourceHash = bundleHashOfFile(sourceBundle);
|
|
@@ -68383,7 +68546,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68383
68546
|
return {
|
|
68384
68547
|
id: "stale_install",
|
|
68385
68548
|
status: "fail",
|
|
68386
|
-
detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${
|
|
68549
|
+
detail: `stale_install: installed workit-cursor ${installed} runs different bytes than the current runtime build (${path39.basename(entry)} hash mismatch)`,
|
|
68387
68550
|
fix: "Re-run install-cursor-plugin.sh — it refreshes the plugin directory with the current build"
|
|
68388
68551
|
};
|
|
68389
68552
|
}
|
|
@@ -68483,7 +68646,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68483
68646
|
}, checkMalformedConfig = (res) => {
|
|
68484
68647
|
const files = [];
|
|
68485
68648
|
for (const name of ["config.json", "youtrack.json", "vcs.json", "workspaces.json"]) {
|
|
68486
|
-
const p =
|
|
68649
|
+
const p = path39.join(res.configDir, name);
|
|
68487
68650
|
if (existsSync16(p))
|
|
68488
68651
|
files.push(p);
|
|
68489
68652
|
}
|
|
@@ -68608,7 +68771,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68608
68771
|
const slot = `${SESSION_HOOK_NAME} hook`;
|
|
68609
68772
|
switch (hook.state) {
|
|
68610
68773
|
case "not_git":
|
|
68611
|
-
return {
|
|
68774
|
+
return {
|
|
68775
|
+
status: "pass",
|
|
68776
|
+
detail: `not inside a git work tree — skipping the ${slot}`
|
|
68777
|
+
};
|
|
68612
68778
|
case "installed":
|
|
68613
68779
|
return {
|
|
68614
68780
|
status: "pass",
|
|
@@ -68701,17 +68867,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68701
68867
|
return { ...installSessionHook(res.cwd, res.env), workspace: workspace.name };
|
|
68702
68868
|
}, isPlaceholder = (p) => {
|
|
68703
68869
|
try {
|
|
68704
|
-
return
|
|
68870
|
+
return readFileSync17(p, "utf8").trim() === TOKEN_PLACEHOLDER2;
|
|
68705
68871
|
} catch {
|
|
68706
68872
|
return false;
|
|
68707
68873
|
}
|
|
68708
68874
|
}, checkCredentialMetadata = (res) => {
|
|
68709
68875
|
const tokenPaths = [];
|
|
68710
|
-
const youtrackJson = readJson(
|
|
68876
|
+
const youtrackJson = readJson(path39.join(res.configDir, "youtrack.json"));
|
|
68711
68877
|
if (youtrackJson) {
|
|
68712
|
-
tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile :
|
|
68713
|
-
} else if (existsSync16(
|
|
68714
|
-
tokenPaths.push(
|
|
68878
|
+
tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path39.join(res.configDir, "youtrack.token"));
|
|
68879
|
+
} else if (existsSync16(path39.join(res.configDir, "youtrack.token"))) {
|
|
68880
|
+
tokenPaths.push(path39.join(res.configDir, "youtrack.token"));
|
|
68715
68881
|
}
|
|
68716
68882
|
if (tokenPaths.length === 0) {
|
|
68717
68883
|
return {
|
|
@@ -68722,7 +68888,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68722
68888
|
}
|
|
68723
68889
|
const problems = [];
|
|
68724
68890
|
for (const raw of tokenPaths) {
|
|
68725
|
-
const p =
|
|
68891
|
+
const p = path39.isAbsolute(raw) ? raw : path39.resolve(res.configDir, raw);
|
|
68726
68892
|
if (!existsSync16(p)) {
|
|
68727
68893
|
problems.push(`${p} is missing`);
|
|
68728
68894
|
continue;
|
|
@@ -68802,7 +68968,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68802
68968
|
},
|
|
68803
68969
|
ghLogin: (e) => {
|
|
68804
68970
|
try {
|
|
68805
|
-
const out = spawnSync16("gh", ["api", "user", "--jq", ".login"], {
|
|
68971
|
+
const out = spawnSync16("gh", ["api", "user", "--jq", ".login"], {
|
|
68972
|
+
encoding: "utf8",
|
|
68973
|
+
env: e
|
|
68974
|
+
});
|
|
68806
68975
|
const login = (out.stdout ?? "").trim();
|
|
68807
68976
|
return out.status === 0 && login ? login : null;
|
|
68808
68977
|
} catch {
|
|
@@ -68837,8 +69006,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68837
69006
|
}), checkGithubIdentity = (res, probes = defaultIdentityProbes(res.env)) => {
|
|
68838
69007
|
const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
|
|
68839
69008
|
if (!remoteHost)
|
|
68840
|
-
return {
|
|
68841
|
-
|
|
69009
|
+
return {
|
|
69010
|
+
id: "github_identity",
|
|
69011
|
+
status: "pass",
|
|
69012
|
+
detail: "no GitHub remote configured"
|
|
69013
|
+
};
|
|
69014
|
+
const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path39.join(res.configDir, "vcs.json"));
|
|
68842
69015
|
const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
|
|
68843
69016
|
if (workspace.error)
|
|
68844
69017
|
return {
|
|
@@ -68849,7 +69022,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68849
69022
|
};
|
|
68850
69023
|
const provider = identityProvider(remoteHost, vcsJson, workspace.provider);
|
|
68851
69024
|
if (provider !== "github")
|
|
68852
|
-
return {
|
|
69025
|
+
return {
|
|
69026
|
+
id: "github_identity",
|
|
69027
|
+
status: "pass",
|
|
69028
|
+
detail: "no GitHub remote configured"
|
|
69029
|
+
};
|
|
68853
69030
|
const cliHost = String(vcsJson?.github?.host ?? "github.com").toLowerCase();
|
|
68854
69031
|
const cliLogin = probes.ghLogin({ ...res.env, GH_HOST: cliHost });
|
|
68855
69032
|
if (!cliLogin)
|
|
@@ -68865,8 +69042,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68865
69042
|
}, checkGitLabIdentity = (res, probes = defaultIdentityProbes(res.env)) => {
|
|
68866
69043
|
const remoteHost = githubHostFromRemote(probes.origin(res.cwd) ?? "");
|
|
68867
69044
|
if (!remoteHost)
|
|
68868
|
-
return {
|
|
68869
|
-
|
|
69045
|
+
return {
|
|
69046
|
+
id: "gitlab_identity",
|
|
69047
|
+
status: "pass",
|
|
69048
|
+
detail: "no GitLab remote configured"
|
|
69049
|
+
};
|
|
69050
|
+
const vcsJson = readJson(res.env.WORKFLOW_VCS_CONFIG ?? path39.join(res.configDir, "vcs.json"));
|
|
68870
69051
|
const workspace = workspaceProviderForIdentity(res.cwd, res.configDir, res.env);
|
|
68871
69052
|
if (workspace.error)
|
|
68872
69053
|
return {
|
|
@@ -68877,7 +69058,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68877
69058
|
};
|
|
68878
69059
|
const provider = identityProvider(remoteHost, vcsJson, workspace.provider);
|
|
68879
69060
|
if (provider !== "gitlab")
|
|
68880
|
-
return {
|
|
69061
|
+
return {
|
|
69062
|
+
id: "gitlab_identity",
|
|
69063
|
+
status: "pass",
|
|
69064
|
+
detail: "no GitLab remote configured"
|
|
69065
|
+
};
|
|
68881
69066
|
const cliHost = String(vcsJson?.gitlab?.host ?? "gitlab.com").toLowerCase();
|
|
68882
69067
|
const cliLogin = probes.glabLogin?.({ ...res.env, GITLAB_HOST: cliHost }) ?? null;
|
|
68883
69068
|
if (!cliLogin)
|
|
@@ -68892,11 +69077,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68892
69077
|
{ surface: "SSH", login: probes.sshLogin(remoteHost) }
|
|
68893
69078
|
]);
|
|
68894
69079
|
}, checkLogWritable = (res) => {
|
|
68895
|
-
const logsDir =
|
|
68896
|
-
const probe =
|
|
69080
|
+
const logsDir = path39.join(res.stateDir, "logs");
|
|
69081
|
+
const probe = path39.join(logsDir, "doctor-probe.tmp");
|
|
68897
69082
|
try {
|
|
68898
|
-
|
|
68899
|
-
|
|
69083
|
+
mkdirSync8(logsDir, { recursive: true, mode: 448 });
|
|
69084
|
+
writeFileSync8(probe, `{"probe":true}
|
|
68900
69085
|
`, { mode: 384 });
|
|
68901
69086
|
return {
|
|
68902
69087
|
id: "log_writable",
|
|
@@ -68926,7 +69111,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68926
69111
|
fix
|
|
68927
69112
|
};
|
|
68928
69113
|
if (lock.state === "absent")
|
|
68929
|
-
return {
|
|
69114
|
+
return {
|
|
69115
|
+
id: "workspace_lock",
|
|
69116
|
+
status: "pass",
|
|
69117
|
+
detail: "no metadata lock held"
|
|
69118
|
+
};
|
|
68930
69119
|
if (lock.state === "stale")
|
|
68931
69120
|
return {
|
|
68932
69121
|
id: "workspace_lock",
|
|
@@ -68971,7 +69160,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68971
69160
|
const counts = { skip: 0, late: 0 };
|
|
68972
69161
|
let text = "";
|
|
68973
69162
|
try {
|
|
68974
|
-
text =
|
|
69163
|
+
text = readFileSync17(path39.join(stateDir, HOOK_SKIPS_FILE), "utf8");
|
|
68975
69164
|
} catch {
|
|
68976
69165
|
return counts;
|
|
68977
69166
|
}
|
|
@@ -68989,7 +69178,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68989
69178
|
}
|
|
68990
69179
|
return counts;
|
|
68991
69180
|
}, checkHookDeadline = (res) => {
|
|
68992
|
-
const file =
|
|
69181
|
+
const file = path39.join(res.hookStateDir, HOOK_SKIPS_FILE);
|
|
68993
69182
|
const { skip, late } = countHookSkips(res.hookStateDir, Date.now() - DAY_MS);
|
|
68994
69183
|
const lateText = late > 0 ? `; ${late} answered past it in full` : "";
|
|
68995
69184
|
if (skip > 0)
|
|
@@ -69020,11 +69209,15 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69020
69209
|
status: "warn",
|
|
69021
69210
|
detail: line.split("; ")[0],
|
|
69022
69211
|
fix: line.split("; ")[1]
|
|
69023
|
-
} : {
|
|
69212
|
+
} : {
|
|
69213
|
+
id: "grant_endpoint",
|
|
69214
|
+
status: "pass",
|
|
69215
|
+
detail: "grants and default endpoint agree"
|
|
69216
|
+
};
|
|
69024
69217
|
}, checkChecksConfig = (res) => {
|
|
69025
69218
|
const root = checkRoot(res.cwd);
|
|
69026
|
-
const current = existsSync16(
|
|
69027
|
-
const legacy = existsSync16(
|
|
69219
|
+
const current = existsSync16(path39.join(root, CHECKS_FILE));
|
|
69220
|
+
const legacy = existsSync16(path39.join(root, LEGACY_CHECKS_FILE));
|
|
69028
69221
|
if (!legacy)
|
|
69029
69222
|
return {
|
|
69030
69223
|
id: "checks_config",
|
|
@@ -69039,8 +69232,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69039
69232
|
};
|
|
69040
69233
|
}, fixChecksConfig = (options = {}) => {
|
|
69041
69234
|
const root = checkRoot(resolve(options).cwd);
|
|
69042
|
-
const from =
|
|
69043
|
-
const to =
|
|
69235
|
+
const from = path39.join(root, LEGACY_CHECKS_FILE);
|
|
69236
|
+
const to = path39.join(root, CHECKS_FILE);
|
|
69044
69237
|
if (!existsSync16(from))
|
|
69045
69238
|
return { action: "none", detail: `no ${LEGACY_CHECKS_FILE} at ${root}` };
|
|
69046
69239
|
if (existsSync16(to))
|
|
@@ -69100,7 +69293,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69100
69293
|
status: "info",
|
|
69101
69294
|
detail: "no Workit Claude Code plugin install recorded — skipping"
|
|
69102
69295
|
};
|
|
69103
|
-
const registry = readJson(
|
|
69296
|
+
const registry = readJson(path39.join(claudeConfigDir(res.home, res.env), "plugins", "installed_plugins.json"));
|
|
69104
69297
|
const recorded = (install) => {
|
|
69105
69298
|
const entries = registry?.plugins?.[install.id];
|
|
69106
69299
|
const entry = Array.isArray(entries) ? entries.find((e) => e?.installPath === install.installPath) : null;
|
|
@@ -69150,7 +69343,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69150
69343
|
}, HOOK_ISSUED_MARKER = "workit-issued-sessions/3", hookRecordsIssued = (root) => {
|
|
69151
69344
|
let files;
|
|
69152
69345
|
try {
|
|
69153
|
-
files =
|
|
69346
|
+
files = readdirSync10(path39.join(root, "dist")).filter((f) => f.endsWith(".js"));
|
|
69154
69347
|
} catch {
|
|
69155
69348
|
return null;
|
|
69156
69349
|
}
|
|
@@ -69158,7 +69351,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69158
69351
|
return null;
|
|
69159
69352
|
return files.some((file) => {
|
|
69160
69353
|
try {
|
|
69161
|
-
return
|
|
69354
|
+
return readFileSync17(path39.join(root, "dist", file), "utf8").includes(HOOK_ISSUED_MARKER);
|
|
69162
69355
|
} catch {
|
|
69163
69356
|
return false;
|
|
69164
69357
|
}
|
|
@@ -69214,7 +69407,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69214
69407
|
env,
|
|
69215
69408
|
timeout: WORKIT_PROBE_TIMEOUT_MS,
|
|
69216
69409
|
shell: true
|
|
69217
|
-
}) : spawnSync16(bin, ["--version"], {
|
|
69410
|
+
}) : spawnSync16(bin, ["--version"], {
|
|
69411
|
+
encoding: "utf8",
|
|
69412
|
+
env,
|
|
69413
|
+
timeout: WORKIT_PROBE_TIMEOUT_MS
|
|
69414
|
+
});
|
|
69218
69415
|
const timedOut = r.error !== undefined && "code" in r.error && r.error.code === "ETIMEDOUT";
|
|
69219
69416
|
const version = r.status === 0 ? (r.stdout ?? "").match(SEMVER)?.[0] ?? null : null;
|
|
69220
69417
|
const probe = { version: timedOut ? null : version, timedOut };
|
|
@@ -69227,14 +69424,22 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69227
69424
|
version: i.version
|
|
69228
69425
|
})),
|
|
69229
69426
|
{ label: "Cursor plugin", version: installedPluginVersion(res) },
|
|
69230
|
-
{
|
|
69427
|
+
{
|
|
69428
|
+
label: "OpenCode plugin",
|
|
69429
|
+
version: installedOpenCodeCache(res)?.version ?? null
|
|
69430
|
+
}
|
|
69231
69431
|
].filter((p) => p.version !== null);
|
|
69232
69432
|
const newest = plugins.reduce((best, p) => best && semverAtLeast(best.version, p.version) ? best : p, null);
|
|
69233
69433
|
const target = newest?.version ?? "latest";
|
|
69234
69434
|
const fix = `npm i -g ${CLI_PACKAGE}@${target} (or run it without a global install: npx -y ${CLI_PACKAGE}@${target} <command>)`;
|
|
69235
69435
|
const bin = findOnPath(pathextNames("workit", res.env), res.env);
|
|
69236
69436
|
if (!bin) {
|
|
69237
|
-
return {
|
|
69437
|
+
return {
|
|
69438
|
+
id: "workit_on_path",
|
|
69439
|
+
status: "warn",
|
|
69440
|
+
detail: "no workit on PATH",
|
|
69441
|
+
fix
|
|
69442
|
+
};
|
|
69238
69443
|
}
|
|
69239
69444
|
const { version, timedOut } = workitVersionAt(bin, res.env);
|
|
69240
69445
|
if (!version) {
|
|
@@ -69269,7 +69474,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69269
69474
|
const file = permissionTarget(host, paths).file;
|
|
69270
69475
|
switch (health.state) {
|
|
69271
69476
|
case "no-host":
|
|
69272
|
-
return {
|
|
69477
|
+
return {
|
|
69478
|
+
id,
|
|
69479
|
+
status: "info",
|
|
69480
|
+
detail: `${host} not found; no permission block needed`
|
|
69481
|
+
};
|
|
69273
69482
|
case "absent":
|
|
69274
69483
|
return {
|
|
69275
69484
|
id,
|
|
@@ -69277,10 +69486,79 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
69277
69486
|
detail: `no Workit permission block in ${file} (optional: \`${fix}\` lets agents run Workit's delivery verbs without a prompt)`
|
|
69278
69487
|
};
|
|
69279
69488
|
case "ok":
|
|
69280
|
-
return {
|
|
69489
|
+
return {
|
|
69490
|
+
id,
|
|
69491
|
+
status: "pass",
|
|
69492
|
+
detail: `Workit's permission block is current in ${file}`
|
|
69493
|
+
};
|
|
69281
69494
|
default:
|
|
69282
69495
|
return { id, status: "warn", detail: health.detail, fix };
|
|
69283
69496
|
}
|
|
69497
|
+
}, checkGrantRules = (host, id) => (res) => {
|
|
69498
|
+
const paths = {
|
|
69499
|
+
home: res.home,
|
|
69500
|
+
env: res.codexHome ? { ...res.env, CODEX_HOME: res.codexHome } : res.env,
|
|
69501
|
+
stateDir: res.stateDir,
|
|
69502
|
+
cwd: res.cwd
|
|
69503
|
+
};
|
|
69504
|
+
let entry = null;
|
|
69505
|
+
try {
|
|
69506
|
+
entry = resolveWorkspaceFrom(res.cwd, res.configDir);
|
|
69507
|
+
} catch {}
|
|
69508
|
+
if (!entry)
|
|
69509
|
+
return {
|
|
69510
|
+
id,
|
|
69511
|
+
status: "info",
|
|
69512
|
+
detail: "no Workit workspace here; no grants to cover"
|
|
69513
|
+
};
|
|
69514
|
+
const { grants } = configuredGrants(entry);
|
|
69515
|
+
const granted = ["push", "pr", "merge"].filter((kind) => kind === "merge" ? Boolean(grants.merge) : grants[kind] !== false);
|
|
69516
|
+
const coverage = permissionCoverage(host, grantedCommands(granted), paths);
|
|
69517
|
+
if (coverage.state === "no-host")
|
|
69518
|
+
return {
|
|
69519
|
+
id,
|
|
69520
|
+
status: "info",
|
|
69521
|
+
detail: `${host} not found; no rules to check`
|
|
69522
|
+
};
|
|
69523
|
+
if (coverage.state === "ok")
|
|
69524
|
+
return {
|
|
69525
|
+
id,
|
|
69526
|
+
status: "pass",
|
|
69527
|
+
detail: `${host} rules cover the granted Workit verbs (${granted.join(", ") || "none"}) and the check and lock verbs`
|
|
69528
|
+
};
|
|
69529
|
+
const shown = coverage.missing.slice(0, 4).join(", ");
|
|
69530
|
+
return {
|
|
69531
|
+
id,
|
|
69532
|
+
status: "warn",
|
|
69533
|
+
detail: `${host} rules in ${coverage.file} do not cover ${shown}${coverage.missing.length > 4 ? ", …" : ""}${existsSync16(coverage.file) ? "" : ` (${coverage.file} does not exist yet; \`workit setup\` writes it)`}: agents get a prompt (or an auto-mode denial) on verbs the workspace grants`,
|
|
69534
|
+
fix: `workit setup --hosts=${host}`
|
|
69535
|
+
};
|
|
69536
|
+
}, ago = (ms) => {
|
|
69537
|
+
const minutes = Math.max(0, Math.round(ms / 60000));
|
|
69538
|
+
if (minutes < 1)
|
|
69539
|
+
return "just now";
|
|
69540
|
+
if (minutes < 60)
|
|
69541
|
+
return `${minutes} min ago`;
|
|
69542
|
+
const hours = Math.round(minutes / 60);
|
|
69543
|
+
return hours < 48 ? `${hours} h ago` : `${Math.round(hours / 24)} d ago`;
|
|
69544
|
+
}, checkHookRuns = (res) => {
|
|
69545
|
+
const runs = readHookRuns(res.hookStateDir).filter((run) => run.event === "PreToolUse" || run.event === "SessionStart");
|
|
69546
|
+
if (runs.length === 0)
|
|
69547
|
+
return {
|
|
69548
|
+
id: "hook_runs",
|
|
69549
|
+
status: "info",
|
|
69550
|
+
detail: "no host hook run recorded yet (counted from this Workit version on; Claude Code launcher fast-path answers and Codex PostToolUse are not counted; start an agent session, then re-run `workit doctor`)"
|
|
69551
|
+
};
|
|
69552
|
+
const now = Date.now();
|
|
69553
|
+
const hosts = [...new Set(runs.map((run) => run.host))].toSorted();
|
|
69554
|
+
const parts = hosts.map((host) => {
|
|
69555
|
+
const seen = ["SessionStart", "PreToolUse"].map((event) => {
|
|
69556
|
+
const run = runs.find((r) => r.host === host && r.event === event);
|
|
69557
|
+
return `${event} ${run ? `${ago(now - Date.parse(run.at))} (${run.count})` : "never"}`;
|
|
69558
|
+
});
|
|
69559
|
+
return `${host}: ${seen.join(", ")}`;
|
|
69560
|
+
});
|
|
69561
|
+
return { id: "hook_runs", status: "pass", detail: parts.join("; ") };
|
|
69284
69562
|
}, RUN_CHECKS, INSTALLER_REQUIRED, runDoctor = (options = {}) => {
|
|
69285
69563
|
const res = resolve(options);
|
|
69286
69564
|
const raw = RUN_CHECKS.map((fn) => fn(res));
|
|
@@ -69332,6 +69610,7 @@ var init_doctor = __esm(() => {
|
|
|
69332
69610
|
init_host_install();
|
|
69333
69611
|
init_skill_manifests();
|
|
69334
69612
|
init_session_hook();
|
|
69613
|
+
init_hook_runs();
|
|
69335
69614
|
init_host_permissions();
|
|
69336
69615
|
OPENCODE_V1_FIX = `OpenCode < ${SUPPORT_MATRIX.opencode.minimum} cannot load Workit 3 (V2 plugin API only): upgrade OpenCode to ${SUPPORT_MATRIX.opencode.minimum}+, or stay on Workit 2.x by pinning "@brainervirus/workit-opencode@2" in the opencode.json "plugin" array`;
|
|
69337
69616
|
AGENT_MARKER_VERSION = /^# Generated by Workit (\d+\.\d+\.\d+)\b/;
|
|
@@ -69371,10 +69650,14 @@ var init_doctor = __esm(() => {
|
|
|
69371
69650
|
checkHeavyScope,
|
|
69372
69651
|
checkHookDeadline,
|
|
69373
69652
|
checkGrantEndpoint,
|
|
69653
|
+
checkHookRuns,
|
|
69374
69654
|
checkChecksConfig,
|
|
69375
69655
|
checkHostPermissions("claude-code", "claude_permissions"),
|
|
69376
69656
|
checkHostPermissions("codex", "codex_permissions"),
|
|
69377
|
-
checkHostPermissions("cursor", "cursor_permissions")
|
|
69657
|
+
checkHostPermissions("cursor", "cursor_permissions"),
|
|
69658
|
+
checkGrantRules("claude-code", "claude_grant_rules"),
|
|
69659
|
+
checkGrantRules("codex", "codex_grant_rules"),
|
|
69660
|
+
checkGrantRules("cursor", "cursor_grant_rules")
|
|
69378
69661
|
];
|
|
69379
69662
|
INSTALLER_REQUIRED = new Set([
|
|
69380
69663
|
"runtime",
|
|
@@ -69394,19 +69677,19 @@ import {
|
|
|
69394
69677
|
cpSync as cpSync4,
|
|
69395
69678
|
existsSync as existsSync17,
|
|
69396
69679
|
lstatSync as lstatSync6,
|
|
69397
|
-
mkdirSync as
|
|
69680
|
+
mkdirSync as mkdirSync9,
|
|
69398
69681
|
mkdtempSync as mkdtempSync2,
|
|
69399
|
-
readFileSync as
|
|
69400
|
-
readdirSync as
|
|
69682
|
+
readFileSync as readFileSync18,
|
|
69683
|
+
readdirSync as readdirSync11,
|
|
69401
69684
|
realpathSync as realpathSync8,
|
|
69402
69685
|
renameSync as renameSync5,
|
|
69403
69686
|
rmSync as rmSync5,
|
|
69404
69687
|
statSync as statSync7,
|
|
69405
|
-
writeFileSync as
|
|
69688
|
+
writeFileSync as writeFileSync9
|
|
69406
69689
|
} from "node:fs";
|
|
69407
69690
|
import { createRequire as createRequire2 } from "node:module";
|
|
69408
69691
|
import os13 from "node:os";
|
|
69409
|
-
import
|
|
69692
|
+
import path40 from "node:path";
|
|
69410
69693
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
|
|
69411
69694
|
function parseList(raw) {
|
|
69412
69695
|
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -69451,7 +69734,7 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
69451
69734
|
const current = readConfigFromDir(state.configDir);
|
|
69452
69735
|
mutations.push({
|
|
69453
69736
|
type: "merge-json",
|
|
69454
|
-
path:
|
|
69737
|
+
path: path40.join(state.configDir, "config.json"),
|
|
69455
69738
|
value: {
|
|
69456
69739
|
locale: values.locale,
|
|
69457
69740
|
localeOptions: current.localeOptions,
|
|
@@ -69477,17 +69760,17 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
69477
69760
|
if (blocked.length === 0) {
|
|
69478
69761
|
mutations.push({
|
|
69479
69762
|
type: "update-workspaces",
|
|
69480
|
-
path:
|
|
69763
|
+
path: path40.join(state.configDir, "workspaces.json"),
|
|
69481
69764
|
entries: values.workspaces,
|
|
69482
69765
|
expectedRevision: diskWorkspaceResult.revision
|
|
69483
69766
|
});
|
|
69484
69767
|
}
|
|
69485
69768
|
}
|
|
69486
69769
|
if (values.baseUrl.trim()) {
|
|
69487
|
-
const ytPath =
|
|
69770
|
+
const ytPath = path40.join(state.configDir, "youtrack.json");
|
|
69488
69771
|
const ytExisting = readConfigRecord(ytPath);
|
|
69489
69772
|
const ytConfigured = configuredTokenPath(ytExisting, "tokenFile");
|
|
69490
|
-
const tokenPath = resolveTokenPath(ytConfigured,
|
|
69773
|
+
const tokenPath = resolveTokenPath(ytConfigured, path40.join(state.configDir, "youtrack.token"), values.tokenPaths?.youtrack);
|
|
69491
69774
|
const draft = youtrackDraft(values, tokenPath);
|
|
69492
69775
|
if (ytConfigured !== null && tokenPath !== ytConfigured) {
|
|
69493
69776
|
delete draft.tokenFile;
|
|
@@ -69515,7 +69798,7 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
69515
69798
|
});
|
|
69516
69799
|
}
|
|
69517
69800
|
if (values.vcsProvider !== "skip") {
|
|
69518
|
-
const vcsPath =
|
|
69801
|
+
const vcsPath = path40.join(state.configDir, "vcs.json");
|
|
69519
69802
|
mutations.push({
|
|
69520
69803
|
type: "merge-json",
|
|
69521
69804
|
path: vcsPath,
|
|
@@ -69544,7 +69827,7 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
69544
69827
|
mutations.push({
|
|
69545
69828
|
type: "install-host",
|
|
69546
69829
|
platform: "cursor",
|
|
69547
|
-
path:
|
|
69830
|
+
path: path40.join(paths.home, ".cursor", "plugins", "local", ".workit-package-bootstrap"),
|
|
69548
69831
|
commands
|
|
69549
69832
|
});
|
|
69550
69833
|
}
|
|
@@ -69557,7 +69840,7 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
69557
69840
|
mutations.push({
|
|
69558
69841
|
type: "install-host",
|
|
69559
69842
|
platform,
|
|
69560
|
-
path: platform === "claude-code" ? claudeInstallRecord(paths.home, env) : platform === "codex" ?
|
|
69843
|
+
path: platform === "claude-code" ? claudeInstallRecord(paths.home, env) : platform === "codex" ? path40.join(env.CODEX_HOME ?? path40.join(paths.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : path40.join(env.PI_CODING_AGENT_DIR ?? path40.join(paths.home, ".pi", "agent"), "settings.json"),
|
|
69561
69844
|
commands
|
|
69562
69845
|
});
|
|
69563
69846
|
}
|
|
@@ -69569,8 +69852,8 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
69569
69852
|
projectRefused = target.refused;
|
|
69570
69853
|
if (target?.ok) {
|
|
69571
69854
|
const root = target.root;
|
|
69572
|
-
const gitignorePath =
|
|
69573
|
-
const existing = existsSync17(gitignorePath) ?
|
|
69855
|
+
const gitignorePath = path40.join(root, ".gitignore");
|
|
69856
|
+
const existing = existsSync17(gitignorePath) ? readFileSync18(gitignorePath, "utf8") : "";
|
|
69574
69857
|
const existingLines = new Set(existing.split(`
|
|
69575
69858
|
`).map((l) => l.trim()).filter(Boolean));
|
|
69576
69859
|
const entries = GITIGNORE_ENTRIES.filter((e) => e.trim() !== "" && !existingLines.has(e.trim()));
|
|
@@ -69607,10 +69890,10 @@ function deepMerge(base, patch) {
|
|
|
69607
69890
|
return out;
|
|
69608
69891
|
}
|
|
69609
69892
|
function applyMutation(m) {
|
|
69610
|
-
const dir =
|
|
69893
|
+
const dir = path40.dirname(m.path);
|
|
69611
69894
|
switch (m.type) {
|
|
69612
69895
|
case "create-file": {
|
|
69613
|
-
|
|
69896
|
+
mkdirSync9(dir, { recursive: true });
|
|
69614
69897
|
const res = writeFileExclusive(m.path, m.content, m.mode);
|
|
69615
69898
|
return res === "created" ? { platform: "core", file: m.path, status: "Installed" } : {
|
|
69616
69899
|
platform: "core",
|
|
@@ -69633,8 +69916,8 @@ function applyMutation(m) {
|
|
|
69633
69916
|
if (existing.kind === "record" && JSON.stringify(existing.value) === JSON.stringify(merged)) {
|
|
69634
69917
|
return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
|
|
69635
69918
|
}
|
|
69636
|
-
|
|
69637
|
-
|
|
69919
|
+
mkdirSync9(dir, { recursive: true });
|
|
69920
|
+
writeFileSync9(m.path, JSON.stringify(merged, null, 2) + `
|
|
69638
69921
|
`, "utf8");
|
|
69639
69922
|
return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
|
|
69640
69923
|
}
|
|
@@ -69675,10 +69958,10 @@ function applyMutation(m) {
|
|
|
69675
69958
|
`;
|
|
69676
69959
|
if (prev === next)
|
|
69677
69960
|
return { platform: "core", file: m.path, status: "Skipped", detail: "already configured" };
|
|
69678
|
-
|
|
69961
|
+
mkdirSync9(dir, { recursive: true });
|
|
69679
69962
|
const tmp = `${m.path}.${process.pid}.tmp`;
|
|
69680
69963
|
try {
|
|
69681
|
-
|
|
69964
|
+
writeFileSync9(tmp, next, "utf8");
|
|
69682
69965
|
if (workspacesRevision(readFileSafe(m.path)) !== m.expectedRevision) {
|
|
69683
69966
|
rmSync5(tmp, { force: true });
|
|
69684
69967
|
return {
|
|
@@ -69713,11 +69996,11 @@ function applyMutation(m) {
|
|
|
69713
69996
|
detail: "all entries already present"
|
|
69714
69997
|
};
|
|
69715
69998
|
}
|
|
69716
|
-
|
|
69999
|
+
mkdirSync9(dir, { recursive: true });
|
|
69717
70000
|
const separator = existing && !existing.endsWith(`
|
|
69718
70001
|
`) ? `
|
|
69719
70002
|
` : "";
|
|
69720
|
-
|
|
70003
|
+
writeFileSync9(m.path, existing + separator + (existing ? `
|
|
69721
70004
|
` : "") + add.join(`
|
|
69722
70005
|
`) + `
|
|
69723
70006
|
`, "utf8");
|
|
@@ -69756,8 +70039,8 @@ function applyMutation(m) {
|
|
|
69756
70039
|
detail: "already configured"
|
|
69757
70040
|
};
|
|
69758
70041
|
}
|
|
69759
|
-
|
|
69760
|
-
|
|
70042
|
+
mkdirSync9(path40.dirname(m.path), { recursive: true });
|
|
70043
|
+
writeFileSync9(m.path, JSON.stringify(merged, null, 2) + `
|
|
69761
70044
|
`, "utf8");
|
|
69762
70045
|
return existing.kind === "record" ? { platform: "core", file: m.path, status: "Configured" } : { platform: "core", file: m.path, status: "Installed" };
|
|
69763
70046
|
}
|
|
@@ -69765,11 +70048,11 @@ function applyMutation(m) {
|
|
|
69765
70048
|
}
|
|
69766
70049
|
function adapterRoot(platform, res, preferInstalledAdapter = false) {
|
|
69767
70050
|
const candidates = [];
|
|
69768
|
-
const stagedCursor =
|
|
70051
|
+
const stagedCursor = path40.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap", "node_modules", "@brainervirus", "workit-cursor");
|
|
69769
70052
|
if (platform === "cursor" && preferInstalledAdapter)
|
|
69770
70053
|
candidates.push(stagedCursor);
|
|
69771
70054
|
if (res.dev) {
|
|
69772
|
-
candidates.push(
|
|
70055
|
+
candidates.push(path40.join(res.dev, "packages", `workit-${platform}`));
|
|
69773
70056
|
} else {
|
|
69774
70057
|
candidates.push(...nodeResolvedAdapters(platform));
|
|
69775
70058
|
if (platform === "cursor")
|
|
@@ -69779,10 +70062,10 @@ function adapterRoot(platform, res, preferInstalledAdapter = false) {
|
|
|
69779
70062
|
if (cached)
|
|
69780
70063
|
candidates.push(cached.root);
|
|
69781
70064
|
}
|
|
69782
|
-
let dir =
|
|
70065
|
+
let dir = path40.resolve(res.cwd);
|
|
69783
70066
|
while (true) {
|
|
69784
|
-
candidates.push(
|
|
69785
|
-
const parent =
|
|
70067
|
+
candidates.push(path40.join(dir, "packages", `workit-${platform}`));
|
|
70068
|
+
const parent = path40.dirname(dir);
|
|
69786
70069
|
if (parent === dir)
|
|
69787
70070
|
break;
|
|
69788
70071
|
dir = parent;
|
|
@@ -69804,7 +70087,7 @@ function applyOpenCode(root, res) {
|
|
|
69804
70087
|
detail: "workit-opencode package has no loadable plugin entry (src/plugin.ts or dist/plugin.js)"
|
|
69805
70088
|
};
|
|
69806
70089
|
}
|
|
69807
|
-
|
|
70090
|
+
mkdirSync9(path40.dirname(res.opencodeConfig), { recursive: true });
|
|
69808
70091
|
const existing = readExisting(res.opencodeConfig);
|
|
69809
70092
|
if (existing.kind === "malformed") {
|
|
69810
70093
|
return {
|
|
@@ -69833,7 +70116,7 @@ function applyOpenCode(root, res) {
|
|
|
69833
70116
|
detail: "already registered"
|
|
69834
70117
|
};
|
|
69835
70118
|
}
|
|
69836
|
-
|
|
70119
|
+
writeFileSync9(res.opencodeConfig, JSON.stringify(merged.config, null, 2) + `
|
|
69837
70120
|
`, "utf8");
|
|
69838
70121
|
return existing.kind === "record" ? {
|
|
69839
70122
|
platform: "opencode",
|
|
@@ -69849,31 +70132,31 @@ function applyOpenCode(root, res) {
|
|
|
69849
70132
|
}
|
|
69850
70133
|
function copyPluginDir(src, dest) {
|
|
69851
70134
|
const realSrc = realpathSync8(src);
|
|
69852
|
-
const marker =
|
|
70135
|
+
const marker = path40.join(dest, ".workit-root");
|
|
69853
70136
|
const destIsLink = existsSync17(dest) && lstatSync6(dest).isSymbolicLink();
|
|
69854
70137
|
const synced = !destIsLink && readFileSafe(marker)?.trim() === realSrc && samePluginContent(realSrc, dest, dest);
|
|
69855
70138
|
if (synced)
|
|
69856
70139
|
return "Skipped";
|
|
69857
70140
|
const hadDir = existsSync17(dest);
|
|
69858
70141
|
const rules = preservedCursorRules(realSrc, dest);
|
|
69859
|
-
const parent =
|
|
69860
|
-
|
|
69861
|
-
const swap = mkdtempSync2(
|
|
69862
|
-
const stage =
|
|
69863
|
-
const backup =
|
|
70142
|
+
const parent = path40.dirname(dest);
|
|
70143
|
+
mkdirSync9(parent, { recursive: true });
|
|
70144
|
+
const swap = mkdtempSync2(path40.join(parent, `.${path40.basename(dest)}.swap-`));
|
|
70145
|
+
const stage = path40.join(swap, "stage");
|
|
70146
|
+
const backup = path40.join(swap, "backup");
|
|
69864
70147
|
try {
|
|
69865
70148
|
cpSync4(realSrc, stage, {
|
|
69866
70149
|
recursive: true,
|
|
69867
|
-
filter: (entry) => !
|
|
70150
|
+
filter: (entry) => !path40.relative(realSrc, entry).split(path40.sep).includes("node_modules")
|
|
69868
70151
|
});
|
|
69869
|
-
const stagedHooks =
|
|
70152
|
+
const stagedHooks = path40.join(stage, CURSOR_HOOKS_FILE);
|
|
69870
70153
|
if (existsSync17(stagedHooks))
|
|
69871
|
-
|
|
70154
|
+
writeFileSync9(stagedHooks, localizedCursorHooks(readFileSync18(stagedHooks), dest));
|
|
69872
70155
|
for (const [name, content] of rules) {
|
|
69873
|
-
|
|
69874
|
-
|
|
70156
|
+
mkdirSync9(path40.join(stage, "rules"), { recursive: true });
|
|
70157
|
+
writeFileSync9(path40.join(stage, "rules", name), content);
|
|
69875
70158
|
}
|
|
69876
|
-
|
|
70159
|
+
writeFileSync9(path40.join(stage, ".workit-root"), realSrc + `
|
|
69877
70160
|
`, "utf8");
|
|
69878
70161
|
if (!samePluginContent(realSrc, stage, dest))
|
|
69879
70162
|
throw new Error("staged adapter content is incomplete");
|
|
@@ -69896,7 +70179,7 @@ function copyPluginDir(src, dest) {
|
|
|
69896
70179
|
return hadDir ? "Configured" : "Installed";
|
|
69897
70180
|
}
|
|
69898
70181
|
function applyCursorSettings(root, res) {
|
|
69899
|
-
|
|
70182
|
+
mkdirSync9(path40.dirname(res.cursorSettings), { recursive: true });
|
|
69900
70183
|
const settingsExisting = readExisting(res.cursorSettings);
|
|
69901
70184
|
if (settingsExisting.kind === "malformed") {
|
|
69902
70185
|
return {
|
|
@@ -69915,7 +70198,7 @@ function applyCursorSettings(root, res) {
|
|
|
69915
70198
|
detail: "already registered"
|
|
69916
70199
|
};
|
|
69917
70200
|
}
|
|
69918
|
-
|
|
70201
|
+
writeFileSync9(res.cursorSettings, JSON.stringify(settings.config, null, 2) + `
|
|
69919
70202
|
`, "utf8");
|
|
69920
70203
|
return {
|
|
69921
70204
|
platform: "cursor",
|
|
@@ -69924,7 +70207,7 @@ function applyCursorSettings(root, res) {
|
|
|
69924
70207
|
};
|
|
69925
70208
|
}
|
|
69926
70209
|
function applyCursorMcp(root, res) {
|
|
69927
|
-
|
|
70210
|
+
mkdirSync9(path40.dirname(res.cursorMcp), { recursive: true });
|
|
69928
70211
|
const mcpExisting = readExisting(res.cursorMcp);
|
|
69929
70212
|
if (mcpExisting.kind === "malformed") {
|
|
69930
70213
|
return {
|
|
@@ -69954,7 +70237,7 @@ function applyCursorMcp(root, res) {
|
|
|
69954
70237
|
detail: "already registered"
|
|
69955
70238
|
};
|
|
69956
70239
|
}
|
|
69957
|
-
|
|
70240
|
+
writeFileSync9(res.cursorMcp, JSON.stringify(mcp.config, null, 2) + `
|
|
69958
70241
|
`, "utf8");
|
|
69959
70242
|
return {
|
|
69960
70243
|
platform: "cursor",
|
|
@@ -70030,7 +70313,7 @@ function applySetupPreview(preview, options = {}) {
|
|
|
70030
70313
|
let cursorCopyOk = false;
|
|
70031
70314
|
for (const mutation of preview.mutations) {
|
|
70032
70315
|
if (mutation.type === "install-host") {
|
|
70033
|
-
const expectedPath = mutation.platform === "cursor" ?
|
|
70316
|
+
const expectedPath = mutation.platform === "cursor" ? path40.join(res.home, ".cursor", "plugins", "local", ".workit-package-bootstrap") : mutation.platform === "codex" ? path40.join(res.env.CODEX_HOME ?? path40.join(res.home, ".codex"), "plugins", "cache", "workflow-toolkit", "workit") : mutation.platform === "pi" ? path40.join(res.env.PI_CODING_AGENT_DIR ?? path40.join(res.home, ".pi", "agent"), "settings.json") : mutation.platform === "claude-code" ? claudeInstallRecord(res.home, res.env) : "";
|
|
70034
70317
|
if (mutation.path !== expectedPath) {
|
|
70035
70318
|
entries.push({
|
|
70036
70319
|
platform: mutation.platform,
|
|
@@ -70142,7 +70425,7 @@ function applySetupPreview(preview, options = {}) {
|
|
|
70142
70425
|
if (preview.projectRefused) {
|
|
70143
70426
|
entries.push({
|
|
70144
70427
|
platform: "core",
|
|
70145
|
-
file:
|
|
70428
|
+
file: path40.resolve(res.cwd),
|
|
70146
70429
|
status: "Skipped",
|
|
70147
70430
|
detail: preview.projectRefused
|
|
70148
70431
|
});
|
|
@@ -70223,12 +70506,12 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
70223
70506
|
return configured ?? fallback;
|
|
70224
70507
|
}, readConfigRecord = (file) => {
|
|
70225
70508
|
try {
|
|
70226
|
-
const value = JSON.parse(
|
|
70509
|
+
const value = JSON.parse(readFileSync18(file, "utf8"));
|
|
70227
70510
|
return isRecord6(value) ? value : null;
|
|
70228
70511
|
} catch {
|
|
70229
70512
|
return null;
|
|
70230
70513
|
}
|
|
70231
|
-
}, claudeInstallRecord = (home, env) =>
|
|
70514
|
+
}, claudeInstallRecord = (home, env) => path40.join(claudeConfigDir(home, env), "plugins", "installed_plugins.json"), resolveSetupPaths = (options, resolvedConfigDir) => {
|
|
70232
70515
|
const env = options.env ?? process.env;
|
|
70233
70516
|
const home = options.home ?? env.HOME ?? os13.homedir();
|
|
70234
70517
|
return {
|
|
@@ -70236,15 +70519,15 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
70236
70519
|
configDir: resolvedConfigDir,
|
|
70237
70520
|
cwd: options.cwd ?? process.cwd(),
|
|
70238
70521
|
env,
|
|
70239
|
-
opencodeConfig: options.opencodeConfig ??
|
|
70240
|
-
cursorSettings: options.cursorSettings ??
|
|
70241
|
-
cursorMcp: options.cursorMcp ??
|
|
70242
|
-
cursorPluginDir: options.cursorPluginDir ??
|
|
70522
|
+
opencodeConfig: options.opencodeConfig ?? path40.join(home, ".config", "opencode", "opencode.json"),
|
|
70523
|
+
cursorSettings: options.cursorSettings ?? path40.join(home, ".cursor", "settings.json"),
|
|
70524
|
+
cursorMcp: options.cursorMcp ?? path40.join(home, ".cursor", "mcp.json"),
|
|
70525
|
+
cursorPluginDir: options.cursorPluginDir ?? path40.join(home, ".cursor", "plugins", "local", "workit")
|
|
70243
70526
|
};
|
|
70244
70527
|
}, isRecord6 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readExisting = (p) => {
|
|
70245
70528
|
let raw;
|
|
70246
70529
|
try {
|
|
70247
|
-
raw =
|
|
70530
|
+
raw = readFileSync18(p, "utf8");
|
|
70248
70531
|
} catch {
|
|
70249
70532
|
if (existsSync17(p))
|
|
70250
70533
|
return { kind: "malformed", error: `${p} is not readable` };
|
|
@@ -70261,7 +70544,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
70261
70544
|
return { kind: "malformed", error: `${p} is not a JSON object` };
|
|
70262
70545
|
}, readFileSafe = (p) => {
|
|
70263
70546
|
try {
|
|
70264
|
-
return
|
|
70547
|
+
return readFileSync18(p, "utf8");
|
|
70265
70548
|
} catch {
|
|
70266
70549
|
return null;
|
|
70267
70550
|
}
|
|
@@ -70276,15 +70559,15 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
70276
70559
|
}
|
|
70277
70560
|
}, isAdapter = (root, platform) => {
|
|
70278
70561
|
try {
|
|
70279
|
-
const pkg = JSON.parse(
|
|
70562
|
+
const pkg = JSON.parse(readFileSync18(path40.join(root, "package.json"), "utf8"));
|
|
70280
70563
|
return pkg.name === `@brainervirus/workit-${platform}`;
|
|
70281
70564
|
} catch {
|
|
70282
70565
|
return false;
|
|
70283
70566
|
}
|
|
70284
70567
|
}, nodeResolvedAdapters = (platform) => {
|
|
70285
70568
|
const name = `@brainervirus/workit-${platform}`;
|
|
70286
|
-
const require2 = createRequire2(
|
|
70287
|
-
return (require2.resolve.paths(name) ?? []).map((dir) =>
|
|
70569
|
+
const require2 = createRequire2(path40.join(packageRoot(), "package.json"));
|
|
70570
|
+
return (require2.resolve.paths(name) ?? []).map((dir) => path40.join(dir, name));
|
|
70288
70571
|
}, realRoot = (root) => {
|
|
70289
70572
|
try {
|
|
70290
70573
|
return realpathSync8(root);
|
|
@@ -70300,7 +70583,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
70300
70583
|
if (!isDevOpenCodeAdapterRoot(root, opts.dev))
|
|
70301
70584
|
return OPENCODE_NPM_PIN;
|
|
70302
70585
|
for (const rel of ["src/plugin.ts", "dist/plugin.js"]) {
|
|
70303
|
-
const entry =
|
|
70586
|
+
const entry = path40.join(root, rel);
|
|
70304
70587
|
if (existsSync17(entry))
|
|
70305
70588
|
return `file://${entry}`;
|
|
70306
70589
|
}
|
|
@@ -70315,25 +70598,25 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
70315
70598
|
}
|
|
70316
70599
|
}, samePluginContent = (src, dest, pluginDir, relative = "") => {
|
|
70317
70600
|
try {
|
|
70318
|
-
for (const entry of
|
|
70601
|
+
for (const entry of readdirSync11(src, { withFileTypes: true })) {
|
|
70319
70602
|
if (entry.name === "node_modules")
|
|
70320
70603
|
continue;
|
|
70321
|
-
const source =
|
|
70322
|
-
const installed =
|
|
70323
|
-
const rel =
|
|
70604
|
+
const source = path40.join(src, entry.name);
|
|
70605
|
+
const installed = path40.join(dest, entry.name);
|
|
70606
|
+
const rel = path40.join(relative, entry.name);
|
|
70324
70607
|
if (entry.isDirectory()) {
|
|
70325
70608
|
if (!statSync7(installed).isDirectory() || !samePluginContent(source, installed, pluginDir, rel))
|
|
70326
70609
|
return false;
|
|
70327
70610
|
} else {
|
|
70328
|
-
const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(
|
|
70329
|
-
if (!expected.equals(
|
|
70611
|
+
const expected = rel === CURSOR_HOOKS_FILE ? localizedCursorHooks(readFileSync18(source), pluginDir) : readFileSync18(source);
|
|
70612
|
+
if (!expected.equals(readFileSync18(installed)))
|
|
70330
70613
|
return false;
|
|
70331
70614
|
}
|
|
70332
70615
|
}
|
|
70333
|
-
for (const entry of
|
|
70334
|
-
if (existsSync17(
|
|
70616
|
+
for (const entry of readdirSync11(dest, { withFileTypes: true })) {
|
|
70617
|
+
if (existsSync17(path40.join(src, entry.name)))
|
|
70335
70618
|
continue;
|
|
70336
|
-
const rel =
|
|
70619
|
+
const rel = path40.join(relative, entry.name);
|
|
70337
70620
|
if (rel === ".workit-root")
|
|
70338
70621
|
continue;
|
|
70339
70622
|
if (relative === "rules" && entry.isFile() && entry.name.endsWith(".mdc"))
|
|
@@ -70346,32 +70629,32 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
70346
70629
|
}
|
|
70347
70630
|
}, preservedCursorRules = (src, dest) => {
|
|
70348
70631
|
const preserved = new Map;
|
|
70349
|
-
const sourceRules =
|
|
70350
|
-
const installedRules =
|
|
70632
|
+
const sourceRules = path40.join(src, "rules");
|
|
70633
|
+
const installedRules = path40.join(dest, "rules");
|
|
70351
70634
|
try {
|
|
70352
|
-
for (const entry of
|
|
70635
|
+
for (const entry of readdirSync11(installedRules, { withFileTypes: true })) {
|
|
70353
70636
|
if (!entry.isFile() || !entry.name.endsWith(".mdc"))
|
|
70354
70637
|
continue;
|
|
70355
|
-
if (existsSync17(
|
|
70638
|
+
if (existsSync17(path40.join(sourceRules, entry.name)))
|
|
70356
70639
|
continue;
|
|
70357
|
-
preserved.set(entry.name,
|
|
70640
|
+
preserved.set(entry.name, readFileSync18(path40.join(installedRules, entry.name)));
|
|
70358
70641
|
}
|
|
70359
70642
|
} catch {}
|
|
70360
70643
|
return preserved;
|
|
70361
70644
|
}, removeLegacyCursorDir = (res) => {
|
|
70362
|
-
const legacy =
|
|
70645
|
+
const legacy = path40.join(res.home, ".cursor", "plugins", "local", "workflow-toolkit");
|
|
70363
70646
|
if (legacy === res.cursorPluginDir || !existsSync17(legacy))
|
|
70364
70647
|
return;
|
|
70365
|
-
const legacyRules =
|
|
70648
|
+
const legacyRules = path40.join(legacy, "rules");
|
|
70366
70649
|
if (existsSync17(legacyRules)) {
|
|
70367
70650
|
try {
|
|
70368
|
-
|
|
70369
|
-
for (const entry of
|
|
70651
|
+
mkdirSync9(path40.join(res.cursorPluginDir, "rules"), { recursive: true });
|
|
70652
|
+
for (const entry of readdirSync11(legacyRules, { withFileTypes: true })) {
|
|
70370
70653
|
if (!entry.isFile() || !entry.name.endsWith(".mdc"))
|
|
70371
70654
|
continue;
|
|
70372
|
-
const target =
|
|
70655
|
+
const target = path40.join(res.cursorPluginDir, "rules", entry.name);
|
|
70373
70656
|
if (!existsSync17(target))
|
|
70374
|
-
copyFileSync5(
|
|
70657
|
+
copyFileSync5(path40.join(legacyRules, entry.name), target);
|
|
70375
70658
|
}
|
|
70376
70659
|
} catch {}
|
|
70377
70660
|
}
|
|
@@ -70405,16 +70688,16 @@ var init_setup = __esm(() => {
|
|
|
70405
70688
|
{ envKey: "WORKFLOW_GITHUB_HOST", affects: "vcs.json github.host" }
|
|
70406
70689
|
];
|
|
70407
70690
|
SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
|
|
70408
|
-
CURSOR_HOOKS_FILE =
|
|
70691
|
+
CURSOR_HOOKS_FILE = path40.join("hooks", "hooks-cursor.json");
|
|
70409
70692
|
});
|
|
70410
70693
|
|
|
70411
70694
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
70412
|
-
import { mkdirSync as
|
|
70413
|
-
import
|
|
70695
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync19, renameSync as renameSync6, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "node:fs";
|
|
70696
|
+
import path41 from "node:path";
|
|
70414
70697
|
import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
|
|
70415
70698
|
function applyWorkspaceBranchPolicy(opts) {
|
|
70416
70699
|
const { workspace_root, env = process.env } = opts;
|
|
70417
|
-
const dir =
|
|
70700
|
+
const dir = path41.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
70418
70701
|
const {
|
|
70419
70702
|
status,
|
|
70420
70703
|
path: wsPath,
|
|
@@ -70426,7 +70709,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
70426
70709
|
if (status === "malformed" || status === "invalid")
|
|
70427
70710
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
70428
70711
|
const detection = detectBranchPolicy(workspace_root);
|
|
70429
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
70712
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path41.basename(workspace_root));
|
|
70430
70713
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
70431
70714
|
const policy = {
|
|
70432
70715
|
preset: detection.preset,
|
|
@@ -70475,10 +70758,10 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
70475
70758
|
ok: false,
|
|
70476
70759
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
70477
70760
|
};
|
|
70478
|
-
|
|
70761
|
+
mkdirSync10(path41.dirname(wsPath), { recursive: true });
|
|
70479
70762
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
70480
70763
|
try {
|
|
70481
|
-
|
|
70764
|
+
writeFileSync10(tmp, JSON.stringify({ ...document2, workspaces: next }, null, 2) + `
|
|
70482
70765
|
`, "utf8");
|
|
70483
70766
|
if (workspacesRevision(readFileSafe2(wsPath)) !== revision) {
|
|
70484
70767
|
rmSync6(tmp, { force: true });
|
|
@@ -70507,7 +70790,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
70507
70790
|
}
|
|
70508
70791
|
var readFileSafe2 = (p) => {
|
|
70509
70792
|
try {
|
|
70510
|
-
return
|
|
70793
|
+
return readFileSync19(p, "utf8");
|
|
70511
70794
|
} catch {
|
|
70512
70795
|
return null;
|
|
70513
70796
|
}
|
|
@@ -70760,7 +71043,7 @@ var init_logic = __esm(() => {
|
|
|
70760
71043
|
|
|
70761
71044
|
// packages/workit-cli/src/wizard-state.ts
|
|
70762
71045
|
import { existsSync as existsSync18, statSync as statSync8 } from "node:fs";
|
|
70763
|
-
import
|
|
71046
|
+
import path42 from "node:path";
|
|
70764
71047
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "node:util";
|
|
70765
71048
|
function resolveBasePath(values, env = process.env) {
|
|
70766
71049
|
return env.WORKFLOW_WORKSPACE_ROOT?.trim() || values.basePath.trim();
|
|
@@ -71287,8 +71570,8 @@ var NEXT, PREV, skipsCustomBranch = (screen, preset) => (screen === "branchAllow
|
|
|
71287
71570
|
const p = value.trim();
|
|
71288
71571
|
if (!p)
|
|
71289
71572
|
return "workspace root path is required";
|
|
71290
|
-
return !
|
|
71291
|
-
}, skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync18(
|
|
71573
|
+
return !path42.isAbsolute(p) || !isDirectory(p) ? `invalid workspace root "${p}" — enter an existing absolute directory` : null;
|
|
71574
|
+
}, skipsBranchPolicy = (screen, basePath) => (screen === "branchPolicy" || screen === "branchPolicyDevelop") && !existsSync18(path42.join(basePath, ".git")), decodeBranchPreset = (value, fallback) => value === "gitflow" || value === "github-flow" || value === "trunk-based" || value === "custom" ? value : fallback, decodeVcsProvider = (value, fallback) => value === "gitlab" || value === "github" || value === "skip" ? value : fallback, decodeIssueTracker = (value, fallback) => value === "youtrack" || value === "github" || value === "gitlab" || value === "none" ? value : fallback, GITHUB_ISSUES, withGithubIssues = (draft, tracker) => tracker === "github" ? { ...draft, issues: { ...GITHUB_ISSUES } } : draft;
|
|
71292
71575
|
var init_wizard_state = __esm(() => {
|
|
71293
71576
|
init_config();
|
|
71294
71577
|
init_workspaces();
|
|
@@ -71347,9 +71630,9 @@ var init_wizard_state = __esm(() => {
|
|
|
71347
71630
|
});
|
|
71348
71631
|
|
|
71349
71632
|
// packages/workit-cli/src/admin/uninstall.ts
|
|
71350
|
-
import { existsSync as existsSync19, readFileSync as
|
|
71633
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20, realpathSync as realpathSync9, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
71351
71634
|
import os14 from "node:os";
|
|
71352
|
-
import
|
|
71635
|
+
import path43 from "node:path";
|
|
71353
71636
|
function cleanCursorSettings(settings, pluginDir) {
|
|
71354
71637
|
const next = { ...settings };
|
|
71355
71638
|
let changed = false;
|
|
@@ -71522,7 +71805,7 @@ function planUninstall(paths = {}) {
|
|
|
71522
71805
|
plan.actions.push({
|
|
71523
71806
|
kind: "edit-json-remove",
|
|
71524
71807
|
path: file,
|
|
71525
|
-
detail: `remove Workit's permission block from ${
|
|
71808
|
+
detail: `remove Workit's permission block from ${path43.basename(file)}`
|
|
71526
71809
|
});
|
|
71527
71810
|
}
|
|
71528
71811
|
if (existsSync19(res.codexRules))
|
|
@@ -71556,8 +71839,8 @@ function applyUninstall(plan, paths = {}) {
|
|
|
71556
71839
|
status = outcome.status;
|
|
71557
71840
|
detail = outcome.detail;
|
|
71558
71841
|
} else if (action.kind === "remove-dir") {
|
|
71559
|
-
const resolved =
|
|
71560
|
-
const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved ===
|
|
71842
|
+
const resolved = path43.resolve(action.path);
|
|
71843
|
+
const allowed = canonicalRemoveDirAllowed(resolved, res) || resolved === path43.resolve(res.codexPluginDir) && path43.basename(resolved) === "workit" && path43.basename(path43.dirname(resolved)) === "plugins";
|
|
71561
71844
|
if (!allowed) {
|
|
71562
71845
|
status = "failed";
|
|
71563
71846
|
detail = `refusing to remove non-canonical plugin directory: ${resolved}`;
|
|
@@ -71599,17 +71882,17 @@ var resolveUninstallPaths = (options = {}) => {
|
|
|
71599
71882
|
claudeSettings: permissionTarget("claude-code", permissionPaths).file,
|
|
71600
71883
|
cursorCliConfig: permissionTarget("cursor", permissionPaths).file,
|
|
71601
71884
|
codexRules: permissionTarget("codex", permissionPaths).file,
|
|
71602
|
-
opencodeConfig: options.opencodeConfig ??
|
|
71603
|
-
cursorSettings: options.cursorSettings ??
|
|
71604
|
-
cursorMcp: options.cursorMcp ??
|
|
71605
|
-
cursorPluginDir: options.cursorPluginDir ??
|
|
71606
|
-
codexPluginDir:
|
|
71607
|
-
piConfig:
|
|
71885
|
+
opencodeConfig: options.opencodeConfig ?? path43.join(home, ".config", "opencode", "opencode.json"),
|
|
71886
|
+
cursorSettings: options.cursorSettings ?? path43.join(home, ".cursor", "settings.json"),
|
|
71887
|
+
cursorMcp: options.cursorMcp ?? path43.join(home, ".cursor", "mcp.json"),
|
|
71888
|
+
cursorPluginDir: options.cursorPluginDir ?? path43.join(home, ".cursor", "plugins", "local", "workit"),
|
|
71889
|
+
codexPluginDir: path43.join(home, ".codex", "plugins", "workit"),
|
|
71890
|
+
piConfig: path43.join(home, ".pi", "config.json")
|
|
71608
71891
|
};
|
|
71609
71892
|
}, isRecord7 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), readJsonRecord = (file) => {
|
|
71610
71893
|
let raw;
|
|
71611
71894
|
try {
|
|
71612
|
-
raw =
|
|
71895
|
+
raw = readFileSync20(file, "utf8");
|
|
71613
71896
|
} catch {
|
|
71614
71897
|
if (existsSync19(file))
|
|
71615
71898
|
return { kind: "malformed", error: `${file} is not readable` };
|
|
@@ -71622,8 +71905,8 @@ var resolveUninstallPaths = (options = {}) => {
|
|
|
71622
71905
|
return { kind: "malformed", error: `${file} is not valid JSON` };
|
|
71623
71906
|
}
|
|
71624
71907
|
}, stripTrailingSep = (p) => {
|
|
71625
|
-
const j =
|
|
71626
|
-
return
|
|
71908
|
+
const j = path43.join(p);
|
|
71909
|
+
return path43.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
|
|
71627
71910
|
}, CURSOR_LEGACY_IDENTITIES, permissionPathsOf = (paths) => {
|
|
71628
71911
|
const env = paths.env ?? process.env;
|
|
71629
71912
|
return {
|
|
@@ -71650,8 +71933,8 @@ var resolveUninstallPaths = (options = {}) => {
|
|
|
71650
71933
|
return cleanPiConfig;
|
|
71651
71934
|
return null;
|
|
71652
71935
|
}, canonicalRemoveDirAllowed = (actionPath, res) => {
|
|
71653
|
-
const expected =
|
|
71654
|
-
if (!(actionPath === expected &&
|
|
71936
|
+
const expected = path43.resolve(res.cursorPluginDir);
|
|
71937
|
+
if (!(actionPath === expected && path43.basename(expected) === "workit" && path43.basename(path43.dirname(expected)) === "local" && path43.basename(path43.dirname(path43.dirname(expected))) === "plugins")) {
|
|
71655
71938
|
return false;
|
|
71656
71939
|
}
|
|
71657
71940
|
try {
|
|
@@ -71674,7 +71957,7 @@ var resolveUninstallPaths = (options = {}) => {
|
|
|
71674
71957
|
`;
|
|
71675
71958
|
let current;
|
|
71676
71959
|
try {
|
|
71677
|
-
current =
|
|
71960
|
+
current = readFileSync20(target, "utf8");
|
|
71678
71961
|
} catch (error) {
|
|
71679
71962
|
return {
|
|
71680
71963
|
status: "failed",
|
|
@@ -71685,7 +71968,7 @@ var resolveUninstallPaths = (options = {}) => {
|
|
|
71685
71968
|
return { status: "skipped", detail: "already clean" };
|
|
71686
71969
|
}
|
|
71687
71970
|
try {
|
|
71688
|
-
|
|
71971
|
+
writeFileSync11(target, serialized, "utf8");
|
|
71689
71972
|
} catch (error) {
|
|
71690
71973
|
return {
|
|
71691
71974
|
status: "failed",
|
|
@@ -71703,7 +71986,7 @@ var resolveUninstallPaths = (options = {}) => {
|
|
|
71703
71986
|
};
|
|
71704
71987
|
const home = paths.home ?? paths.env?.HOME ?? os14.homedir();
|
|
71705
71988
|
const env = paths.env ?? process.env;
|
|
71706
|
-
const recorded = claudeWorkitInstalls(home, env, action.cwd).some((install) => install.id === id && install.scope === scope && (install.projectPath === null ? action.cwd === null :
|
|
71989
|
+
const recorded = claudeWorkitInstalls(home, env, action.cwd).some((install) => install.id === id && install.scope === scope && (install.projectPath === null ? action.cwd === null : path43.resolve(install.projectPath) === path43.resolve(action.cwd ?? "")));
|
|
71707
71990
|
if (!recorded)
|
|
71708
71991
|
return { status: "skipped", detail: `${id} (${scope} scope) is no longer installed` };
|
|
71709
71992
|
const executable = findHostExecutable("claude", { home, env });
|
|
@@ -74465,14 +74748,14 @@ import {
|
|
|
74465
74748
|
existsSync as existsSync20,
|
|
74466
74749
|
mkdtempSync as mkdtempSync3,
|
|
74467
74750
|
rmSync as rmSync8,
|
|
74468
|
-
readFileSync as
|
|
74469
|
-
mkdirSync as
|
|
74751
|
+
readFileSync as readFileSync21,
|
|
74752
|
+
mkdirSync as mkdirSync11,
|
|
74470
74753
|
copyFileSync as copyFileSync6,
|
|
74471
74754
|
chmodSync as chmodSync2,
|
|
74472
74755
|
cpSync as cpSync5,
|
|
74473
74756
|
lstatSync as lstatSync7
|
|
74474
74757
|
} from "node:fs";
|
|
74475
|
-
import
|
|
74758
|
+
import path44 from "node:path";
|
|
74476
74759
|
import os16 from "node:os";
|
|
74477
74760
|
import { createHash as createHash15 } from "node:crypto";
|
|
74478
74761
|
function previewUpgrade(hosts = HOSTS2, deps = {}) {
|
|
@@ -74496,9 +74779,9 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
|
|
|
74496
74779
|
for (const file of [
|
|
74497
74780
|
paths.opencodeConfig,
|
|
74498
74781
|
paths.opencodeConfig + "c",
|
|
74499
|
-
|
|
74500
|
-
|
|
74501
|
-
|
|
74782
|
+
path44.join(home, ".cursor/settings.json"),
|
|
74783
|
+
path44.join(home, ".cursor/mcp.json"),
|
|
74784
|
+
path44.join(home, ".cursor/plugins/local/workit/package.json"),
|
|
74502
74785
|
paths.codexConfig,
|
|
74503
74786
|
paths.piSettings
|
|
74504
74787
|
])
|
|
@@ -74513,16 +74796,16 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
|
|
|
74513
74796
|
});
|
|
74514
74797
|
const installedSources = sources(paths, run, hosts);
|
|
74515
74798
|
if (installedSources.some((source) => source.host === "opencode")) {
|
|
74516
|
-
const scratch = mkdtempSync3(
|
|
74799
|
+
const scratch = mkdtempSync3(path44.join(os16.tmpdir(), "workit-upgrade-opencode-"));
|
|
74517
74800
|
let probe;
|
|
74518
74801
|
try {
|
|
74519
74802
|
probe = runner({
|
|
74520
74803
|
...deps,
|
|
74521
74804
|
env: {
|
|
74522
74805
|
...deps.env,
|
|
74523
|
-
XDG_DATA_HOME:
|
|
74524
|
-
XDG_STATE_HOME:
|
|
74525
|
-
XDG_CACHE_HOME:
|
|
74806
|
+
XDG_DATA_HOME: path44.join(scratch, "data"),
|
|
74807
|
+
XDG_STATE_HOME: path44.join(scratch, "state"),
|
|
74808
|
+
XDG_CACHE_HOME: path44.join(scratch, "cache")
|
|
74526
74809
|
}
|
|
74527
74810
|
})("opencode", ["--version"]);
|
|
74528
74811
|
} finally {
|
|
@@ -74568,7 +74851,7 @@ function previewUpgrade(hosts = HOSTS2, deps = {}) {
|
|
|
74568
74851
|
env: deps.env,
|
|
74569
74852
|
cwd: home,
|
|
74570
74853
|
packageVersion: latest.stdout.trim(),
|
|
74571
|
-
targetDir:
|
|
74854
|
+
targetDir: path44.join(home, ".cursor/plugins/local/workit")
|
|
74572
74855
|
});
|
|
74573
74856
|
plan.entries.push({
|
|
74574
74857
|
host: source.host,
|
|
@@ -74598,11 +74881,11 @@ function applyUpgrade(plan, deps = {}) {
|
|
|
74598
74881
|
ok: false,
|
|
74599
74882
|
error: "Stop the selected running hosts before upgrading their loaded plugins"
|
|
74600
74883
|
};
|
|
74601
|
-
const backup =
|
|
74602
|
-
|
|
74884
|
+
const backup = path44.join(home, ".local/state/workit/upgrades", new Date().toISOString().replaceAll(":", "-"));
|
|
74885
|
+
mkdirSync11(backup, { recursive: true, mode: 448 });
|
|
74603
74886
|
for (const file of Object.keys(plan.configDigests))
|
|
74604
74887
|
if (existsSync20(file)) {
|
|
74605
|
-
const target =
|
|
74888
|
+
const target = path44.join(backup, createHash15("sha256").update(file).digest("hex").slice(0, 12) + "-" + path44.basename(file));
|
|
74606
74889
|
copyFileSync6(file, target);
|
|
74607
74890
|
chmodSync2(target, 384);
|
|
74608
74891
|
}
|
|
@@ -74619,9 +74902,9 @@ function applyUpgrade(plan, deps = {}) {
|
|
|
74619
74902
|
`, 384);
|
|
74620
74903
|
}
|
|
74621
74904
|
const run = runner(deps);
|
|
74622
|
-
const cursorRoot =
|
|
74905
|
+
const cursorRoot = path44.join(home, ".cursor/plugins/local/workit");
|
|
74623
74906
|
if (plan.entries.some((entry) => entry.host === "cursor") && existsSync20(cursorRoot))
|
|
74624
|
-
cpSync5(cursorRoot,
|
|
74907
|
+
cpSync5(cursorRoot, path44.join(backup, "cursor-package"), { recursive: true });
|
|
74625
74908
|
for (const entry of plan.entries) {
|
|
74626
74909
|
if (entry.host === "opencode")
|
|
74627
74910
|
return {
|
|
@@ -74641,7 +74924,7 @@ function applyUpgrade(plan, deps = {}) {
|
|
|
74641
74924
|
{
|
|
74642
74925
|
type: "install-host",
|
|
74643
74926
|
platform: "cursor",
|
|
74644
|
-
path:
|
|
74927
|
+
path: path44.join(home, ".cursor/plugins/local/.workit-package-bootstrap"),
|
|
74645
74928
|
commands: entry.commands.map((command) => ({
|
|
74646
74929
|
...command,
|
|
74647
74930
|
purpose: command.purpose ?? "Upgrade Cursor"
|
|
@@ -74650,7 +74933,7 @@ function applyUpgrade(plan, deps = {}) {
|
|
|
74650
74933
|
{
|
|
74651
74934
|
type: "install-adapter",
|
|
74652
74935
|
platform: "cursor",
|
|
74653
|
-
path:
|
|
74936
|
+
path: path44.join(home, ".cursor/plugins/local/workit")
|
|
74654
74937
|
}
|
|
74655
74938
|
]
|
|
74656
74939
|
};
|
|
@@ -74664,7 +74947,7 @@ function applyUpgrade(plan, deps = {}) {
|
|
|
74664
74947
|
return { exitCode: outcome.status ?? 1, stdout: outcome.stdout, stderr: "" };
|
|
74665
74948
|
}
|
|
74666
74949
|
});
|
|
74667
|
-
const pkg = object2(
|
|
74950
|
+
const pkg = object2(path44.join(home, ".cursor/plugins/local/workit/package.json"));
|
|
74668
74951
|
if (!result.ok || pkg?.version !== entry.latest)
|
|
74669
74952
|
return {
|
|
74670
74953
|
ok: false,
|
|
@@ -74698,15 +74981,15 @@ function applyUpgrade(plan, deps = {}) {
|
|
|
74698
74981
|
const currentRoot = run("npm", ["root", "--global"]);
|
|
74699
74982
|
if (currentRoot.status !== 0 || currentRoot.stdout.trim() !== plan.cli.root)
|
|
74700
74983
|
return { ok: false, backup, error: "Global npm location changed; preview again" };
|
|
74701
|
-
const cliBackup =
|
|
74702
|
-
cpSync5(
|
|
74984
|
+
const cliBackup = path44.join(backup, "cli-package");
|
|
74985
|
+
cpSync5(path44.join(plan.cli.root, "@brainervirus/workit-cli"), cliBackup, { recursive: true });
|
|
74703
74986
|
if (run("npm", ["install", "--global", `@brainervirus/workit-cli@${plan.cli.latest}`]).status !== 0)
|
|
74704
74987
|
return {
|
|
74705
74988
|
ok: false,
|
|
74706
74989
|
backup,
|
|
74707
|
-
error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${
|
|
74990
|
+
error: `CLI update failed; preserved package at ${cliBackup}; use npm to repair or run node ${path44.join(cliBackup, "dist/index.js")}`
|
|
74708
74991
|
};
|
|
74709
|
-
const installed = object2(
|
|
74992
|
+
const installed = object2(path44.join(plan.cli.root, "@brainervirus/workit-cli/package.json"));
|
|
74710
74993
|
if (installed?.name !== "@brainervirus/workit-cli" || installed.version !== plan.cli.latest)
|
|
74711
74994
|
return {
|
|
74712
74995
|
ok: false,
|
|
@@ -74731,12 +75014,12 @@ async function runUpgradeCommand(argv, deps = {}) {
|
|
|
74731
75014
|
if (flags.has("--cli")) {
|
|
74732
75015
|
const run = runner(deps);
|
|
74733
75016
|
const root = run("npm", ["root", "--global"]);
|
|
74734
|
-
if (root.status !== 0 || !
|
|
75017
|
+
if (root.status !== 0 || !path44.isAbsolute(root.stdout.trim())) {
|
|
74735
75018
|
plan.blocked.push("Global npm location could not be verified");
|
|
74736
75019
|
} else {
|
|
74737
|
-
const packageFile =
|
|
75020
|
+
const packageFile = path44.join(root.stdout.trim(), "@brainervirus/workit-cli/package.json");
|
|
74738
75021
|
const pkg = object2(packageFile);
|
|
74739
|
-
if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync7(
|
|
75022
|
+
if (pkg?.name !== "@brainervirus/workit-cli" || lstatSync7(path44.dirname(packageFile)).isSymbolicLink()) {
|
|
74740
75023
|
plan.skipped.push({
|
|
74741
75024
|
host: "cli",
|
|
74742
75025
|
reason: "No managed global CLI install (local links preserved); use npx @brainervirus/workit-cli@latest"
|
|
@@ -74859,37 +75142,37 @@ var HOSTS2, upgradePaths = (deps) => {
|
|
|
74859
75142
|
...process.env,
|
|
74860
75143
|
...deps.home || deps.env?.HOME ? {
|
|
74861
75144
|
HOME: home,
|
|
74862
|
-
CODEX_HOME:
|
|
74863
|
-
PI_CODING_AGENT_DIR:
|
|
74864
|
-
XDG_CONFIG_HOME:
|
|
75145
|
+
CODEX_HOME: path44.join(home, ".codex"),
|
|
75146
|
+
PI_CODING_AGENT_DIR: path44.join(home, ".pi/agent"),
|
|
75147
|
+
XDG_CONFIG_HOME: path44.join(home, ".config"),
|
|
74865
75148
|
WORKFLOW_TOOLKIT_CONFIG: undefined,
|
|
74866
75149
|
WORKFLOW_TOOLKIT_CONFIG_DIR: undefined
|
|
74867
75150
|
} : {},
|
|
74868
75151
|
...deps.env,
|
|
74869
75152
|
HOME: home
|
|
74870
75153
|
};
|
|
74871
|
-
const xdg = env.XDG_CONFIG_HOME ||
|
|
75154
|
+
const xdg = env.XDG_CONFIG_HOME || path44.join(home, ".config");
|
|
74872
75155
|
return {
|
|
74873
75156
|
home,
|
|
74874
75157
|
env,
|
|
74875
|
-
configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
74876
|
-
opencodeConfig:
|
|
74877
|
-
piSettings:
|
|
74878
|
-
codexConfig:
|
|
75158
|
+
configDir: env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path44.join(xdg, "workit"),
|
|
75159
|
+
opencodeConfig: path44.join(xdg, "opencode/opencode.json"),
|
|
75160
|
+
piSettings: path44.join(env.PI_CODING_AGENT_DIR ?? path44.join(home, ".pi/agent"), "settings.json"),
|
|
75161
|
+
codexConfig: path44.join(env.CODEX_HOME ?? path44.join(home, ".codex"), "config.toml")
|
|
74879
75162
|
};
|
|
74880
75163
|
}, object2 = (file) => {
|
|
74881
75164
|
if (!existsSync20(file))
|
|
74882
75165
|
return null;
|
|
74883
75166
|
let data;
|
|
74884
75167
|
try {
|
|
74885
|
-
data = JSON.parse(
|
|
75168
|
+
data = JSON.parse(readFileSync21(file, "utf8"));
|
|
74886
75169
|
} catch {
|
|
74887
75170
|
throw new Error(`${file} is not valid JSON; use the native host to inspect JSONC settings`);
|
|
74888
75171
|
}
|
|
74889
75172
|
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
74890
75173
|
throw new Error(`${file} is not a configuration object`);
|
|
74891
75174
|
return data;
|
|
74892
|
-
}, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(
|
|
75175
|
+
}, digest2 = (file) => existsSync20(file) ? createHash15("sha256").update(readFileSync21(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
|
|
74893
75176
|
const { home, env } = upgradePaths(deps);
|
|
74894
75177
|
let step;
|
|
74895
75178
|
try {
|
|
@@ -74915,9 +75198,9 @@ var HOSTS2, upgradePaths = (deps) => {
|
|
|
74915
75198
|
for (const pin of plugins)
|
|
74916
75199
|
if (typeof pin === "string" && isWorkitPlugin(pin))
|
|
74917
75200
|
result.push(classify("opencode", pin));
|
|
74918
|
-
const cursor = hosts.includes("cursor") ? object2(
|
|
75201
|
+
const cursor = hosts.includes("cursor") ? object2(path44.join(home, ".cursor/plugins/local/workit/package.json")) : null;
|
|
74919
75202
|
if (cursor?.name === "@brainervirus/workit-cursor") {
|
|
74920
|
-
const mcp = object2(
|
|
75203
|
+
const mcp = object2(path44.join(home, ".cursor/mcp.json"));
|
|
74921
75204
|
const args = mcp?.mcpServers?.workit?.args ?? [];
|
|
74922
75205
|
const selector = args.find((arg) => typeof arg === "string" && arg.startsWith("--package="));
|
|
74923
75206
|
result.push(classify("cursor", typeof selector === "string" ? selector.slice(10) : "local"));
|
|
@@ -74930,7 +75213,7 @@ var HOSTS2, upgradePaths = (deps) => {
|
|
|
74930
75213
|
if (typeof source !== "string")
|
|
74931
75214
|
continue;
|
|
74932
75215
|
const npm = /^(?:npm:)?@brainervirus\/workit-pi(?:@[^/]+)?$/.test(source);
|
|
74933
|
-
const local =
|
|
75216
|
+
const local = path44.isAbsolute(source) && source.replaceAll("\\", "/").endsWith("/packages/workit-pi") && object2(path44.join(source, "package.json"))?.name === "@brainervirus/workit-pi";
|
|
74934
75217
|
if (npm || local)
|
|
74935
75218
|
result.push(classify("pi", source));
|
|
74936
75219
|
}
|
|
@@ -74977,9 +75260,9 @@ var HOSTS2, upgradePaths = (deps) => {
|
|
|
74977
75260
|
}, piInstalledVersion = (listing, selector) => {
|
|
74978
75261
|
const lines = listing.split(/\r?\n/).map((line) => line.trim());
|
|
74979
75262
|
const index = lines.indexOf(selector);
|
|
74980
|
-
if (index < 0 || !lines[index + 1] || !
|
|
75263
|
+
if (index < 0 || !lines[index + 1] || !path44.isAbsolute(lines[index + 1]))
|
|
74981
75264
|
return;
|
|
74982
|
-
const pkg = object2(
|
|
75265
|
+
const pkg = object2(path44.join(lines[index + 1], "package.json"));
|
|
74983
75266
|
return pkg?.name === "@brainervirus/workit-pi" ? pkg.version : undefined;
|
|
74984
75267
|
}, versionAtLeast = (installed, latest) => {
|
|
74985
75268
|
if (!/^\d+\.\d+\.\d+$/.test(installed))
|
|
@@ -75033,7 +75316,7 @@ var init_launch = __esm(() => {
|
|
|
75033
75316
|
// packages/workit-core/src/knowledge.ts
|
|
75034
75317
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
75035
75318
|
import fs20 from "node:fs";
|
|
75036
|
-
import
|
|
75319
|
+
import path45 from "node:path";
|
|
75037
75320
|
function lintKnowledge(root) {
|
|
75038
75321
|
const seen = new Set;
|
|
75039
75322
|
const unreadable = [];
|
|
@@ -75137,7 +75420,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
|
|
|
75137
75420
|
const end = lines.indexOf("---", 1);
|
|
75138
75421
|
return end === -1 ? lines : [...lines.slice(0, end + 1).map(() => ""), ...lines.slice(end + 1)];
|
|
75139
75422
|
}, read = (root, file) => {
|
|
75140
|
-
const full =
|
|
75423
|
+
const full = path45.join(root, file);
|
|
75141
75424
|
try {
|
|
75142
75425
|
fs20.lstatSync(full);
|
|
75143
75426
|
} catch {
|
|
@@ -75178,7 +75461,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
|
|
|
75178
75461
|
}
|
|
75179
75462
|
}, brokenLinks = (root, doc, tracked) => {
|
|
75180
75463
|
const findings = [];
|
|
75181
|
-
const base =
|
|
75464
|
+
const base = path45.dirname(path45.join(root, doc.file));
|
|
75182
75465
|
const missing = (target, line, how) => findings.push({
|
|
75183
75466
|
rule: "broken-link",
|
|
75184
75467
|
file: doc.file,
|
|
@@ -75197,7 +75480,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
|
|
|
75197
75480
|
const local = localTarget(target);
|
|
75198
75481
|
if (!local)
|
|
75199
75482
|
continue;
|
|
75200
|
-
const resolved = local.startsWith("/") ?
|
|
75483
|
+
const resolved = local.startsWith("/") ? path45.join(root, local) : path45.resolve(base, local);
|
|
75201
75484
|
if (!exists2(resolved))
|
|
75202
75485
|
missing(target, index + 1, "link");
|
|
75203
75486
|
}
|
|
@@ -75207,9 +75490,9 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
|
|
|
75207
75490
|
continue;
|
|
75208
75491
|
const relative = /^\.\.?\//.test(candidate);
|
|
75209
75492
|
const first = candidate.split("/")[0];
|
|
75210
|
-
if (!relative && !(tracked ? tracked.has(first) : exists2(
|
|
75493
|
+
if (!relative && !(tracked ? tracked.has(first) : exists2(path45.join(root, first))))
|
|
75211
75494
|
continue;
|
|
75212
|
-
const resolved = relative ?
|
|
75495
|
+
const resolved = relative ? path45.resolve(base, candidate) : path45.join(root, candidate);
|
|
75213
75496
|
if (!exists2(resolved))
|
|
75214
75497
|
missing(span, index + 1, "pointer");
|
|
75215
75498
|
}
|
|
@@ -75535,7 +75818,7 @@ import { spawn, spawnSync as spawnSync19 } from "node:child_process";
|
|
|
75535
75818
|
import { createHash as createHash16 } from "node:crypto";
|
|
75536
75819
|
import fs21 from "node:fs";
|
|
75537
75820
|
import os17 from "node:os";
|
|
75538
|
-
import
|
|
75821
|
+
import path46 from "node:path";
|
|
75539
75822
|
import { StringDecoder } from "node:string_decoder";
|
|
75540
75823
|
function tailLines(text, lines = TAIL_LINES) {
|
|
75541
75824
|
const all = text.replace(/\r\n?/gu, `
|
|
@@ -75655,7 +75938,7 @@ function planSpawn(argv, options) {
|
|
|
75655
75938
|
return { file: argv[0], args: [], verbatim: false, shell: true };
|
|
75656
75939
|
if (options.platform !== "win32")
|
|
75657
75940
|
return { file: argv[0], args: argv.slice(1), verbatim: false, shell: false };
|
|
75658
|
-
const win =
|
|
75941
|
+
const win = path46.win32;
|
|
75659
75942
|
const isFile = options.isFile ?? ((file) => {
|
|
75660
75943
|
try {
|
|
75661
75944
|
return fs21.statSync(file).isFile();
|
|
@@ -75817,10 +76100,10 @@ function runCheckCommand(argv, options) {
|
|
|
75817
76100
|
function storeLog(storeRoot, log) {
|
|
75818
76101
|
const digest = logDigest(log);
|
|
75819
76102
|
const ref = `blobs/logs/${digest.slice("sha256:".length)}.log`;
|
|
75820
|
-
const file =
|
|
76103
|
+
const file = path46.join(storeRoot, ref);
|
|
75821
76104
|
try {
|
|
75822
76105
|
if (!fs21.existsSync(file)) {
|
|
75823
|
-
fs21.mkdirSync(
|
|
76106
|
+
fs21.mkdirSync(path46.dirname(file), { recursive: true });
|
|
75824
76107
|
const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
75825
76108
|
fs21.writeFileSync(temp, log, { mode: 384 });
|
|
75826
76109
|
fs21.renameSync(temp, file);
|
|
@@ -75833,7 +76116,7 @@ function storeLog(storeRoot, log) {
|
|
|
75833
76116
|
function pruneCheckLogs(storeRoot, options = {}) {
|
|
75834
76117
|
const limits = { ...LOG_RETENTION, ...options.retention };
|
|
75835
76118
|
const now = options.now ?? Date.now();
|
|
75836
|
-
const dir =
|
|
76119
|
+
const dir = path46.join(storeRoot, "blobs", "logs");
|
|
75837
76120
|
const report = { removed: 0, removedBytes: 0, kept: 0 };
|
|
75838
76121
|
let names;
|
|
75839
76122
|
try {
|
|
@@ -75843,7 +76126,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
|
|
|
75843
76126
|
}
|
|
75844
76127
|
const files = names.flatMap((name) => {
|
|
75845
76128
|
try {
|
|
75846
|
-
const stat = fs21.statSync(
|
|
76129
|
+
const stat = fs21.statSync(path46.join(dir, name));
|
|
75847
76130
|
return stat.isFile() ? [{ name, size: stat.size, mtimeMs: stat.mtimeMs }] : [];
|
|
75848
76131
|
} catch {
|
|
75849
76132
|
return [];
|
|
@@ -75862,7 +76145,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
|
|
|
75862
76145
|
continue;
|
|
75863
76146
|
if (!options.dryRun)
|
|
75864
76147
|
try {
|
|
75865
|
-
fs21.rmSync(
|
|
76148
|
+
fs21.rmSync(path46.join(dir, file.name), { force: true });
|
|
75866
76149
|
} catch {
|
|
75867
76150
|
continue;
|
|
75868
76151
|
}
|
|
@@ -76413,12 +76696,12 @@ var init_workspace = __esm(() => {
|
|
|
76413
76696
|
|
|
76414
76697
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
76415
76698
|
import fs22 from "node:fs";
|
|
76416
|
-
import
|
|
76699
|
+
import path47 from "node:path";
|
|
76417
76700
|
function gitTop(cwd) {
|
|
76418
|
-
for (let dir =
|
|
76419
|
-
if (fs22.existsSync(
|
|
76701
|
+
for (let dir = path47.resolve(cwd);; dir = path47.dirname(dir)) {
|
|
76702
|
+
if (fs22.existsSync(path47.join(dir, ".git")))
|
|
76420
76703
|
return dir;
|
|
76421
|
-
if (
|
|
76704
|
+
if (path47.dirname(dir) === dir)
|
|
76422
76705
|
return null;
|
|
76423
76706
|
}
|
|
76424
76707
|
}
|
|
@@ -76460,18 +76743,18 @@ var init_heavy_rules = () => {};
|
|
|
76460
76743
|
// packages/workit-core/src/core/heavy.ts
|
|
76461
76744
|
import fs23 from "node:fs";
|
|
76462
76745
|
import os18 from "node:os";
|
|
76463
|
-
import
|
|
76746
|
+
import path48 from "node:path";
|
|
76464
76747
|
function stateDir2(env, platform = process.platform) {
|
|
76465
76748
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
76466
76749
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
76467
76750
|
if (env.XDG_STATE_HOME)
|
|
76468
|
-
return
|
|
76751
|
+
return path48.join(env.XDG_STATE_HOME, "workit");
|
|
76469
76752
|
const home = env.HOME || os18.homedir();
|
|
76470
76753
|
if (platform === "darwin")
|
|
76471
|
-
return
|
|
76754
|
+
return path48.join(home, "Library", "Application Support", "workit");
|
|
76472
76755
|
if (platform === "win32")
|
|
76473
|
-
return
|
|
76474
|
-
return
|
|
76756
|
+
return path48.join(env.LOCALAPPDATA ?? path48.join(home, "AppData", "Local"), "workit");
|
|
76757
|
+
return path48.join(home, ".local", "state", "workit");
|
|
76475
76758
|
}
|
|
76476
76759
|
function positional(words, from, values = PM_VALUES) {
|
|
76477
76760
|
let index = from;
|
|
@@ -76608,10 +76891,10 @@ function parsePattern(pattern) {
|
|
|
76608
76891
|
}
|
|
76609
76892
|
function machinePatterns(env) {
|
|
76610
76893
|
const value = readJson2(heavyCommandsFile(env));
|
|
76611
|
-
return isRecord8(value) ?
|
|
76894
|
+
return isRecord8(value) ? stringList2(value.patterns) : [];
|
|
76612
76895
|
}
|
|
76613
76896
|
function repoPatterns(cwd) {
|
|
76614
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
76897
|
+
return repoHeavyCommands(gitTop(cwd) ?? path48.resolve(cwd));
|
|
76615
76898
|
}
|
|
76616
76899
|
function heavyLines(lines, options) {
|
|
76617
76900
|
const found = [];
|
|
@@ -76690,13 +76973,13 @@ function leaseRewrite(command) {
|
|
|
76690
76973
|
return null;
|
|
76691
76974
|
return `${prefix}workit check --heavy -- ${rest}`;
|
|
76692
76975
|
}
|
|
76693
|
-
var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) =>
|
|
76976
|
+
var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path48.join(stateDir2(env), HEAVY_COMMANDS_FILE), HEAVY_SCRIPT, INFO_FLAGS, WATCH_FLAG, WATCH_SCRIPT, sub = (...names) => new Set(names), TOOLS, PM_VALUES, baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, ""), WORKIT, PYTHONS, NPM_INSTALL, NPM_RUN, NPM_TEST, EXEC, globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u"), matches2 = (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])), readJson2 = (file) => {
|
|
76694
76977
|
try {
|
|
76695
76978
|
return JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
76696
76979
|
} catch {
|
|
76697
76980
|
return;
|
|
76698
76981
|
}
|
|
76699
|
-
}, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value),
|
|
76982
|
+
}, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), stringList2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], linesOf = (command, dialect) => {
|
|
76700
76983
|
try {
|
|
76701
76984
|
return commandLines(command, dialect);
|
|
76702
76985
|
} catch {
|
|
@@ -76837,7 +77120,7 @@ var init_heavy = __esm(() => {
|
|
|
76837
77120
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
76838
77121
|
import fs24 from "node:fs";
|
|
76839
77122
|
import os19 from "node:os";
|
|
76840
|
-
import
|
|
77123
|
+
import path49 from "node:path";
|
|
76841
77124
|
function memoryValue(value, host) {
|
|
76842
77125
|
const text = typeof value === "number" ? String(value) : typeof value === "string" ? value : "";
|
|
76843
77126
|
const percent = PERCENT.exec(text.trim());
|
|
@@ -77018,7 +77301,7 @@ function tryOnce(options, record, reclaimed) {
|
|
|
77018
77301
|
const text = `${JSON.stringify(record)}
|
|
77019
77302
|
`;
|
|
77020
77303
|
for (let slot = 0;slot < options.slots; slot += 1) {
|
|
77021
|
-
const file =
|
|
77304
|
+
const file = path49.join(options.dir, `slot-${slot}.json`);
|
|
77022
77305
|
for (let attempt = 0;attempt < 2; attempt += 1) {
|
|
77023
77306
|
try {
|
|
77024
77307
|
const fd = fs24.openSync(file, "wx", 384);
|
|
@@ -77143,7 +77426,7 @@ function heldLease(dir, id, options = {}) {
|
|
|
77143
77426
|
for (const name of names) {
|
|
77144
77427
|
if (!/^slot-\d+\.json$/u.test(name))
|
|
77145
77428
|
continue;
|
|
77146
|
-
const lease = parseLease(readText5(
|
|
77429
|
+
const lease = parseLease(readText5(path49.join(dir, name)));
|
|
77147
77430
|
if (lease?.id === id && !staleReason(lease, options.probe) && (options.ancestor ?? isAncestor)(lease.pid))
|
|
77148
77431
|
return lease;
|
|
77149
77432
|
}
|
|
@@ -77198,10 +77481,10 @@ async function runScoped(run, wrapper, sinks) {
|
|
|
77198
77481
|
}
|
|
77199
77482
|
function firstNotice(env, session, key, now = Date.now()) {
|
|
77200
77483
|
const id = (session ?? `ppid-${process.ppid}`).replace(/[^A-Za-z0-9_.-]/gu, "_").slice(0, 120);
|
|
77201
|
-
const dir =
|
|
77484
|
+
const dir = path49.join(stateDir2(env), "notices");
|
|
77202
77485
|
try {
|
|
77203
77486
|
fs24.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
77204
|
-
fs24.writeFileSync(
|
|
77487
|
+
fs24.writeFileSync(path49.join(dir, `${key}-${id}`), `${new Date(now).toISOString()}
|
|
77205
77488
|
`, {
|
|
77206
77489
|
flag: "wx",
|
|
77207
77490
|
mode: 384
|
|
@@ -77211,7 +77494,7 @@ function firstNotice(env, session, key, now = Date.now()) {
|
|
|
77211
77494
|
}
|
|
77212
77495
|
try {
|
|
77213
77496
|
for (const name of fs24.readdirSync(dir)) {
|
|
77214
|
-
const file =
|
|
77497
|
+
const file = path49.join(dir, name);
|
|
77215
77498
|
if (now - fs24.statSync(file).mtimeMs > NOTICE_TTL_MS)
|
|
77216
77499
|
fs24.rmSync(file, { force: true });
|
|
77217
77500
|
}
|
|
@@ -77221,7 +77504,7 @@ function firstNotice(env, session, key, now = Date.now()) {
|
|
|
77221
77504
|
function leaseSlots(dir, slots, probe = {}) {
|
|
77222
77505
|
const states = [];
|
|
77223
77506
|
for (let slot = 0;slot < slots; slot += 1) {
|
|
77224
|
-
const lease = parseLease(readText5(
|
|
77507
|
+
const lease = parseLease(readText5(path49.join(dir, `slot-${slot}.json`)));
|
|
77225
77508
|
states.push({
|
|
77226
77509
|
slot,
|
|
77227
77510
|
holder: lease ? holderOf(lease) : null,
|
|
@@ -77233,7 +77516,7 @@ function leaseSlots(dir, slots, probe = {}) {
|
|
|
77233
77516
|
var DEFAULT_HEAVY_CHECKS, DEFAULT_SLOTS = 1, DEFAULT_WAIT_S = 600, DEFAULT_MEMORY_SHARE = 0.4, DEFAULT_CPU_SHARE = 0.7, MAX_SLOTS = 64, UNREADABLE_TTL_MS = 30000, GUARD_TTL_MS = 30000, hostShape = () => ({
|
|
77234
77517
|
totalMemBytes: os19.totalmem(),
|
|
77235
77518
|
cpus: Math.max(1, os19.availableParallelism?.() ?? os19.cpus().length)
|
|
77236
|
-
}), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) =>
|
|
77519
|
+
}), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) => path49.join(stateDir2(env), "leases"), readText5 = (file) => {
|
|
77237
77520
|
try {
|
|
77238
77521
|
return fs24.readFileSync(file, "utf8");
|
|
77239
77522
|
} catch {
|
|
@@ -77288,7 +77571,7 @@ __export(exports_check, {
|
|
|
77288
77571
|
workspaceResources: () => workspaceResources
|
|
77289
77572
|
});
|
|
77290
77573
|
import os20 from "node:os";
|
|
77291
|
-
import
|
|
77574
|
+
import path50 from "node:path";
|
|
77292
77575
|
function parse6(argv) {
|
|
77293
77576
|
const split = argv.indexOf("--");
|
|
77294
77577
|
const head = split >= 0 ? argv.slice(0, split) : [...argv];
|
|
@@ -77486,7 +77769,7 @@ function hostToolWaitCapMs(env) {
|
|
|
77486
77769
|
}
|
|
77487
77770
|
async function workspaceResources(io) {
|
|
77488
77771
|
const env = io.env;
|
|
77489
|
-
const dir = env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
77772
|
+
const dir = env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path50.join(env.XDG_CONFIG_HOME || path50.join(env.HOME || os20.homedir(), ".config"), "workit");
|
|
77490
77773
|
try {
|
|
77491
77774
|
await Promise.resolve().then(() => init_workspaces());
|
|
77492
77775
|
const workspace = resolveWorkspaceFrom(io.cwd, dir, env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
|
|
@@ -77605,7 +77888,7 @@ async function run12(argv, io) {
|
|
|
77605
77888
|
if (options.shell && command.length !== 1)
|
|
77606
77889
|
return emit(io, fail2("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
|
|
77607
77890
|
}
|
|
77608
|
-
const relCwd = posix(
|
|
77891
|
+
const relCwd = posix(path50.relative(top, runCwd));
|
|
77609
77892
|
const configured = !options.shell && matchesNamedCheck(config, name, command, relCwd);
|
|
77610
77893
|
const shadowed = !configured && name !== null ? config.checks.find((item) => item.name === name) : undefined;
|
|
77611
77894
|
if (shadowed)
|
|
@@ -77667,7 +77950,7 @@ async function run12(argv, io) {
|
|
|
77667
77950
|
const modifiedWorktree = key.tree !== null && treeAfter !== key.tree;
|
|
77668
77951
|
const store = storeRoot(io.cwd);
|
|
77669
77952
|
const blob = store.ok ? storeLog(store.value.root, result.log) : null;
|
|
77670
|
-
const logPath = store.ok && blob ?
|
|
77953
|
+
const logPath = store.ok && blob ? path50.join(store.value.root, blob.ref) : null;
|
|
77671
77954
|
const tail = tailLines(result.log, TAIL_LINES);
|
|
77672
77955
|
const passed = result.exitCode === 0 && !result.timedOut;
|
|
77673
77956
|
const bounded = boundedArgv(command);
|
|
@@ -77780,7 +78063,7 @@ async function run12(argv, io) {
|
|
|
77780
78063
|
return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
|
|
77781
78064
|
return 0;
|
|
77782
78065
|
}
|
|
77783
|
-
var USAGE5 = 'workit check <name> [--full] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <s>] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(
|
|
78066
|
+
var USAGE5 = 'workit check <name> [--full] [--no-lease --reason "<why>"] | workit check [--name <n>] [--shell] [--heavy] [--full] [--wait <s>] [--timeout <s>] [--task <id>] [--json] -- <cmd…>', OUTPUT_LINES = 60, MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path50.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS, HOST_TOOL_WAIT_CAP_MS = 1e5;
|
|
77784
78067
|
var init_check = __esm(() => {
|
|
77785
78068
|
init_check_config();
|
|
77786
78069
|
init_checks3();
|
|
@@ -77806,7 +78089,7 @@ __export(exports_lock, {
|
|
|
77806
78089
|
run: () => run13
|
|
77807
78090
|
});
|
|
77808
78091
|
import fs25 from "node:fs";
|
|
77809
|
-
import
|
|
78092
|
+
import path51 from "node:path";
|
|
77810
78093
|
async function settingsFor(io) {
|
|
77811
78094
|
const workspace = await workspaceResources(io);
|
|
77812
78095
|
const config = loadCheckConfig(io.cwd);
|
|
@@ -77888,7 +78171,7 @@ function readPatterns(file) {
|
|
|
77888
78171
|
return { patterns: [] };
|
|
77889
78172
|
}
|
|
77890
78173
|
function writePatterns(file, value) {
|
|
77891
|
-
fs25.mkdirSync(
|
|
78174
|
+
fs25.mkdirSync(path51.dirname(file), { recursive: true, mode: 448 });
|
|
77892
78175
|
const temp = `${file}.${process.pid}.tmp`;
|
|
77893
78176
|
fs25.writeFileSync(temp, `${JSON.stringify(value, null, 2)}
|
|
77894
78177
|
`, { mode: 384 });
|
|
@@ -77974,9 +78257,9 @@ var exports_models = {};
|
|
|
77974
78257
|
__export(exports_models, {
|
|
77975
78258
|
run: () => run14
|
|
77976
78259
|
});
|
|
77977
|
-
import { existsSync as existsSync22, readdirSync as
|
|
78260
|
+
import { existsSync as existsSync22, readdirSync as readdirSync12, readFileSync as readFileSync22, statSync as statSync9 } from "node:fs";
|
|
77978
78261
|
import os21 from "node:os";
|
|
77979
|
-
import
|
|
78262
|
+
import path52 from "node:path";
|
|
77980
78263
|
async function run14(argv, io) {
|
|
77981
78264
|
const [sub, ...args] = argv.filter((arg) => arg !== "--json");
|
|
77982
78265
|
if (sub !== "show")
|
|
@@ -78037,7 +78320,7 @@ var USAGE7 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
|
|
|
78037
78320
|
const found = new Set;
|
|
78038
78321
|
let text = "";
|
|
78039
78322
|
try {
|
|
78040
|
-
text =
|
|
78323
|
+
text = readFileSync22(file, "utf8");
|
|
78041
78324
|
} catch {
|
|
78042
78325
|
return found;
|
|
78043
78326
|
}
|
|
@@ -78051,24 +78334,24 @@ var USAGE7 = "workit models show [--role <role>] [--tier <tier>] [--host <host>]
|
|
|
78051
78334
|
} catch {}
|
|
78052
78335
|
return found;
|
|
78053
78336
|
}, matches3 = (ran, configured) => ran.toLowerCase().includes(configured.toLowerCase()), usageReport = (cwd, env, days) => {
|
|
78054
|
-
const projects =
|
|
78337
|
+
const projects = path52.join(env.CLAUDE_CONFIG_DIR || path52.join(env.HOME || os21.homedir(), ".claude"), "projects");
|
|
78055
78338
|
const since = Date.now() - days * 86400000;
|
|
78056
78339
|
const types = {};
|
|
78057
78340
|
let agents = 0;
|
|
78058
78341
|
const dirs = (dir) => {
|
|
78059
78342
|
try {
|
|
78060
|
-
return
|
|
78343
|
+
return readdirSync12(dir).map((name) => path52.join(dir, name));
|
|
78061
78344
|
} catch {
|
|
78062
78345
|
return [];
|
|
78063
78346
|
}
|
|
78064
78347
|
};
|
|
78065
78348
|
for (const project of dirs(projects))
|
|
78066
78349
|
for (const session of dirs(project))
|
|
78067
|
-
for (const meta of dirs(
|
|
78350
|
+
for (const meta of dirs(path52.join(session, "subagents")).filter((f) => f.endsWith(".meta.json"))) {
|
|
78068
78351
|
try {
|
|
78069
78352
|
if (statSync9(meta).mtimeMs < since)
|
|
78070
78353
|
continue;
|
|
78071
|
-
const agentType = JSON.parse(
|
|
78354
|
+
const agentType = JSON.parse(readFileSync22(meta, "utf8")).agentType;
|
|
78072
78355
|
const transcript = meta.replace(/\.meta\.json$/, ".jsonl");
|
|
78073
78356
|
if (typeof agentType !== "string" || !existsSync22(transcript))
|
|
78074
78357
|
continue;
|
|
@@ -78209,8 +78492,8 @@ var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock)
|
|
|
78209
78492
|
|
|
78210
78493
|
// packages/workit-core/src/git/ops.ts
|
|
78211
78494
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
78212
|
-
import { existsSync as existsSync23, readFileSync as
|
|
78213
|
-
import
|
|
78495
|
+
import { existsSync as existsSync23, readFileSync as readFileSync23 } from "node:fs";
|
|
78496
|
+
import path53 from "node:path";
|
|
78214
78497
|
function operationInProgress(cwd) {
|
|
78215
78498
|
const markers = [
|
|
78216
78499
|
["rebase-merge", "rebase"],
|
|
@@ -78223,18 +78506,18 @@ function operationInProgress(cwd) {
|
|
|
78223
78506
|
if (!run.ok)
|
|
78224
78507
|
return null;
|
|
78225
78508
|
const found = run.stdout.split(`
|
|
78226
|
-
`).map((line) =>
|
|
78509
|
+
`).map((line) => path53.resolve(cwd, line.trim()));
|
|
78227
78510
|
const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
|
|
78228
78511
|
for (const [index, [, operation]] of markers.entries()) {
|
|
78229
78512
|
const marker = found[index];
|
|
78230
78513
|
if (!marker || !existsSync23(marker))
|
|
78231
78514
|
continue;
|
|
78232
|
-
if (index === 1 && existsSync23(
|
|
78515
|
+
if (index === 1 && existsSync23(path53.join(marker, "applying")))
|
|
78233
78516
|
return { operation: "am", commitAllowed: false, branch: null };
|
|
78234
|
-
if (index === 0 && existsSync23(
|
|
78517
|
+
if (index === 0 && existsSync23(path53.join(marker, "amend")) && !conflicted()) {
|
|
78235
78518
|
let headName = "";
|
|
78236
78519
|
try {
|
|
78237
|
-
headName =
|
|
78520
|
+
headName = readFileSync23(path53.join(marker, "head-name"), "utf8").trim();
|
|
78238
78521
|
} catch {}
|
|
78239
78522
|
const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
|
|
78240
78523
|
return { operation, commitAllowed: branch !== null, branch };
|
|
@@ -78562,7 +78845,7 @@ function gitCommit(cwd, input) {
|
|
|
78562
78845
|
if (paths.some((value) => value.startsWith("-") || !value.trim()))
|
|
78563
78846
|
return failure3("invalid_input", "paths must not be empty or start with -");
|
|
78564
78847
|
const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
|
|
78565
|
-
const addable = paths.filter((value) => existsSync23(
|
|
78848
|
+
const addable = paths.filter((value) => existsSync23(path53.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
|
|
78566
78849
|
if (addable.length) {
|
|
78567
78850
|
const added = git5(cwd, ["add", "-A", "--", ...addable]);
|
|
78568
78851
|
if (!added.ok)
|
|
@@ -79101,12 +79384,12 @@ var init_ops = __esm(() => {
|
|
|
79101
79384
|
|
|
79102
79385
|
// packages/workit-core/src/verdict-cover.ts
|
|
79103
79386
|
import fs26 from "node:fs";
|
|
79104
|
-
import
|
|
79387
|
+
import path54 from "node:path";
|
|
79105
79388
|
function chainsAbove(cwd, branch) {
|
|
79106
79389
|
const root = storeRoot(cwd);
|
|
79107
79390
|
if (!root.ok)
|
|
79108
79391
|
return [];
|
|
79109
|
-
const dir =
|
|
79392
|
+
const dir = path54.join(root.value.root, "stacks");
|
|
79110
79393
|
let names = [];
|
|
79111
79394
|
try {
|
|
79112
79395
|
names = fs26.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -79117,7 +79400,7 @@ function chainsAbove(cwd, branch) {
|
|
|
79117
79400
|
for (const name of names.toSorted()) {
|
|
79118
79401
|
let parsed;
|
|
79119
79402
|
try {
|
|
79120
|
-
parsed = JSON.parse(fs26.readFileSync(
|
|
79403
|
+
parsed = JSON.parse(fs26.readFileSync(path54.join(dir, name), "utf8"));
|
|
79121
79404
|
} catch {
|
|
79122
79405
|
continue;
|
|
79123
79406
|
}
|
|
@@ -79212,10 +79495,10 @@ var init_pr_lane = __esm(() => {
|
|
|
79212
79495
|
|
|
79213
79496
|
// packages/workit-core/src/forge/reruns.ts
|
|
79214
79497
|
import fs27 from "node:fs";
|
|
79215
|
-
import
|
|
79498
|
+
import path55 from "node:path";
|
|
79216
79499
|
function rerunLogPath(cwd) {
|
|
79217
79500
|
const common = gitCommonDir(cwd);
|
|
79218
|
-
return common ?
|
|
79501
|
+
return common ? path55.join(common, "workit", "ci-reruns.jsonl") : null;
|
|
79219
79502
|
}
|
|
79220
79503
|
function rerunCounts(cwd, key) {
|
|
79221
79504
|
const counts = new Map;
|
|
@@ -79250,7 +79533,7 @@ function recordReruns(cwd, rows) {
|
|
|
79250
79533
|
if (!file)
|
|
79251
79534
|
return false;
|
|
79252
79535
|
try {
|
|
79253
|
-
fs27.mkdirSync(
|
|
79536
|
+
fs27.mkdirSync(path55.dirname(file), { recursive: true });
|
|
79254
79537
|
fs27.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
|
|
79255
79538
|
`).join(""));
|
|
79256
79539
|
return true;
|
|
@@ -79263,7 +79546,7 @@ function withRerunLock(cwd, fn) {
|
|
|
79263
79546
|
if (!file)
|
|
79264
79547
|
return fn();
|
|
79265
79548
|
const lock = `${file.slice(0, -".jsonl".length)}.lock`;
|
|
79266
|
-
fs27.mkdirSync(
|
|
79549
|
+
fs27.mkdirSync(path55.dirname(lock), { recursive: true });
|
|
79267
79550
|
const until = Date.now() + LOCK_WAIT_MS2;
|
|
79268
79551
|
for (;; ) {
|
|
79269
79552
|
try {
|
|
@@ -79952,6 +80235,22 @@ function implicitDeleteSkip(cwd, resolved, doc, branch) {
|
|
|
79952
80235
|
return `open PR ${children.data.map((n) => `#${n}`).join(", ")} targets ${branch} (a stack)`;
|
|
79953
80236
|
return null;
|
|
79954
80237
|
}
|
|
80238
|
+
function baseCiState(cwd, resolved, base) {
|
|
80239
|
+
if (!isProtectedTarget(cwd, base))
|
|
80240
|
+
return { ok: true, note: null };
|
|
80241
|
+
const read = resolved.forge.branchChecks(base);
|
|
80242
|
+
if (!read.ok)
|
|
80243
|
+
return { ok: true, note: `base ${base} CI not read (${read.error}); merging anyway` };
|
|
80244
|
+
const { requiredKnown } = read.data;
|
|
80245
|
+
const failing = read.data.checks.filter((check) => check.state === "failing" && (requiredKnown ? check.required === true : check.required !== false && check.conclusion === "failure"));
|
|
80246
|
+
if (failing.length)
|
|
80247
|
+
return { ok: false, failing, sha: read.data.sha, requiredKnown };
|
|
80248
|
+
const pending = read.data.checks.filter((check) => check.state === "pending");
|
|
80249
|
+
return {
|
|
80250
|
+
ok: true,
|
|
80251
|
+
note: pending.length ? `base ${base} CI is still running (${pending.map((check) => check.name).join(", ")}); merging without waiting` : null
|
|
80252
|
+
};
|
|
80253
|
+
}
|
|
79955
80254
|
async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
79956
80255
|
const grant = requireGrant(cwd, "merge");
|
|
79957
80256
|
if (!grant.allowed)
|
|
@@ -79961,6 +80260,9 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
79961
80260
|
return failure3("invalid_input", "--unverified needs --reason <why the user asked for it>");
|
|
79962
80261
|
if (bypass && !grant.allowUnverified)
|
|
79963
80262
|
return refuse(`unverified_refused: workspace ${grant.workspace ? `"${grant.workspace}"` : "(none)"} grants merge: "verified", which never merges without an accepted independent verdict`, "a non-author session verifies the head (workit-review), then merge without --unverified", { reason: "unverified_refused" });
|
|
80263
|
+
const baseRed = input.baseRedOk ?? null;
|
|
80264
|
+
if (baseRed && !baseRed.reason.trim())
|
|
80265
|
+
return failure3("invalid_input", "--base-red-ok needs --reason <why merging onto a red base is fine>");
|
|
79964
80266
|
const number = selectPr(cwd, resolved, { pr: input.pr });
|
|
79965
80267
|
if (!number.ok)
|
|
79966
80268
|
return number;
|
|
@@ -80045,6 +80347,15 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
80045
80347
|
const lane = gate.verification === "proportional" ? `, ${laneLabel(gate)}` : "";
|
|
80046
80348
|
return refuse(gate.need === "self" ? `NEEDS_VERDICT: ${label} has no verdict for ${head.slice(0, 12)}${lane}: the author's own --self verdict is enough (${check.accepted.reasons.join(", ")})` : `NEEDS_VERDICT: ${label} has no accepted independent verdict for ${head.slice(0, 12)}${lane} (${check.accepted.reasons.join(", ")})`, gate.need === "self" ? `after an observed passing \`workit check test\`, the author runs: workit ledger verdict tests-verified --self --how "<what you ran>" --branch ${branch} (or a verifier records one)${bypassHint}` : `an independent session verifies the head and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${branch}${bypassHint}`, { reason: "needs_verdict", verdict: summary });
|
|
80047
80349
|
}
|
|
80350
|
+
const baseCi = baseCiState(cwd, resolved, doc.base);
|
|
80351
|
+
let baseRedOverride = null;
|
|
80352
|
+
if (!baseCi.ok) {
|
|
80353
|
+
const names = baseCi.failing.map((failed) => failed.name).join(", ");
|
|
80354
|
+
const url = baseCi.failing.find((failed) => failed.url)?.url ?? null;
|
|
80355
|
+
if (!baseRed)
|
|
80356
|
+
return refuse(`base_red: ${doc.base} at ${baseCi.sha?.slice(0, 12) ?? "its head"} has failing CI (${names}${baseCi.requiredKnown ? "" : "; the required checks could not be read, so every failed check counts"})${url ? `: ${url}` : ""}; merging ${label} onto it would land on a red base`, `fix ${doc.base} first, or, if the user accepts it: workit pr merge${input.pr ? ` --pr ${doc.number}` : ""} --base-red-ok --reason "<why>"`, { reason: "base_red", blockers: baseCi.failing.map((failed) => failed.name) });
|
|
80357
|
+
baseRedOverride = baseRed;
|
|
80358
|
+
}
|
|
80048
80359
|
const squashMessage = squashMessageFor(cwd, resolved, input, doc);
|
|
80049
80360
|
if (squashMessage && !squashMessage.ok)
|
|
80050
80361
|
return squashMessage;
|
|
@@ -80060,6 +80371,7 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
80060
80371
|
review: verdict.review,
|
|
80061
80372
|
lane: verdict.lane,
|
|
80062
80373
|
...unverified ? { unverified: unverified.recorded } : {},
|
|
80374
|
+
...baseRedOverride ? { baseRedOk: baseRedOverride.reason } : {},
|
|
80063
80375
|
grant: grant.source
|
|
80064
80376
|
});
|
|
80065
80377
|
if (!intent.ok)
|
|
@@ -80088,6 +80400,7 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
80088
80400
|
review: verdict.review,
|
|
80089
80401
|
lane: verdict.lane,
|
|
80090
80402
|
...unverified ? { unverified: unverified.recorded } : {},
|
|
80403
|
+
...baseRedOverride ? { baseRedOk: baseRedOverride.reason } : {},
|
|
80091
80404
|
grant: grant.source,
|
|
80092
80405
|
intent: intentId,
|
|
80093
80406
|
verified: false
|
|
@@ -80144,6 +80457,10 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
80144
80457
|
mergeSha: merged.data.mergeSha,
|
|
80145
80458
|
verdict,
|
|
80146
80459
|
unverified,
|
|
80460
|
+
baseCi: {
|
|
80461
|
+
note: baseRedOverride ? `base ${doc.base} CI is red; merged anyway (--base-red-ok: ${baseRedOverride.reason})` : baseCi.ok ? baseCi.note : null,
|
|
80462
|
+
override: baseRedOverride
|
|
80463
|
+
},
|
|
80147
80464
|
grant: { source: grant.source },
|
|
80148
80465
|
deletedBranch,
|
|
80149
80466
|
recorded,
|
|
@@ -80210,7 +80527,7 @@ var init_pr_ops = __esm(() => {
|
|
|
80210
80527
|
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
80211
80528
|
import { createHash as createHash17, randomBytes as randomBytes4 } from "node:crypto";
|
|
80212
80529
|
import fs28 from "node:fs";
|
|
80213
|
-
import
|
|
80530
|
+
import path56 from "node:path";
|
|
80214
80531
|
function worktreeOf(cwd, branch) {
|
|
80215
80532
|
const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
|
|
80216
80533
|
if (!list.ok)
|
|
@@ -80254,7 +80571,7 @@ function gitRestack(cwd, input) {
|
|
|
80254
80571
|
if (!scratch)
|
|
80255
80572
|
return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
|
|
80256
80573
|
fs28.mkdirSync(scratch, { recursive: true });
|
|
80257
|
-
dir =
|
|
80574
|
+
dir = path56.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
|
|
80258
80575
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
80259
80576
|
if (!added.ok)
|
|
80260
80577
|
return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
|
|
@@ -80295,7 +80612,7 @@ function pruneScratch(cwd) {
|
|
|
80295
80612
|
if (!scratch || !fs28.existsSync(scratch))
|
|
80296
80613
|
return;
|
|
80297
80614
|
for (const name of fs28.readdirSync(scratch)) {
|
|
80298
|
-
const dir =
|
|
80615
|
+
const dir = path56.join(scratch, name);
|
|
80299
80616
|
if (!rebaseInProgress(dir) && !trackedDirt(dir))
|
|
80300
80617
|
gitRun3(cwd, ["worktree", "remove", "--force", dir]);
|
|
80301
80618
|
}
|
|
@@ -80305,7 +80622,7 @@ function stacksDir(cwd) {
|
|
|
80305
80622
|
const root = storeRoot(cwd);
|
|
80306
80623
|
if (!root.ok)
|
|
80307
80624
|
return stackFail(root.code, root.error, root.unblock);
|
|
80308
|
-
return { ok: true, data:
|
|
80625
|
+
return { ok: true, data: path56.join(root.value.root, "stacks") };
|
|
80309
80626
|
}
|
|
80310
80627
|
function parseStack(raw) {
|
|
80311
80628
|
if (!isRecord12(raw) || !Array.isArray(raw.branches))
|
|
@@ -80357,7 +80674,7 @@ function readStack(cwd, name) {
|
|
|
80357
80674
|
const dir = stacksDir(cwd);
|
|
80358
80675
|
if (!dir.ok)
|
|
80359
80676
|
return dir;
|
|
80360
|
-
const file =
|
|
80677
|
+
const file = path56.join(dir.data, fileFor(name));
|
|
80361
80678
|
let text;
|
|
80362
80679
|
try {
|
|
80363
80680
|
text = fs28.readFileSync(file, "utf8");
|
|
@@ -80378,7 +80695,7 @@ function writeStack(cwd, stack, now = new Date) {
|
|
|
80378
80695
|
const dir = stacksDir(cwd);
|
|
80379
80696
|
if (!dir.ok)
|
|
80380
80697
|
return dir;
|
|
80381
|
-
const file =
|
|
80698
|
+
const file = path56.join(dir.data, fileFor(stack.name));
|
|
80382
80699
|
const temp = `${file}.${process.pid}.tmp`;
|
|
80383
80700
|
try {
|
|
80384
80701
|
fs28.mkdirSync(dir.data, { recursive: true });
|
|
@@ -80406,7 +80723,7 @@ function listStacks(cwd) {
|
|
|
80406
80723
|
for (const name of names) {
|
|
80407
80724
|
let parsed = null;
|
|
80408
80725
|
try {
|
|
80409
|
-
parsed = parseStack(JSON.parse(fs28.readFileSync(
|
|
80726
|
+
parsed = parseStack(JSON.parse(fs28.readFileSync(path56.join(dir.data, name), "utf8")));
|
|
80410
80727
|
} catch {
|
|
80411
80728
|
parsed = null;
|
|
80412
80729
|
}
|
|
@@ -80465,7 +80782,7 @@ function takeLock(lock) {
|
|
|
80465
80782
|
host: localLockHost(),
|
|
80466
80783
|
nonce: randomBytes4(8).toString("hex")
|
|
80467
80784
|
};
|
|
80468
|
-
fs28.writeFileSync(
|
|
80785
|
+
fs28.writeFileSync(path56.join(lock, OWNER), JSON.stringify(owner));
|
|
80469
80786
|
return owner;
|
|
80470
80787
|
}
|
|
80471
80788
|
function reapLock(lock, observed) {
|
|
@@ -80502,7 +80819,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
|
|
|
80502
80819
|
return dir;
|
|
80503
80820
|
if (!validName(name))
|
|
80504
80821
|
return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
|
|
80505
|
-
const lock =
|
|
80822
|
+
const lock = path56.join(dir.data, `${fileFor(name)}.lock`);
|
|
80506
80823
|
try {
|
|
80507
80824
|
fs28.mkdirSync(dir.data, { recursive: true });
|
|
80508
80825
|
} catch (error) {
|
|
@@ -80532,7 +80849,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
|
|
|
80532
80849
|
const touch = () => {
|
|
80533
80850
|
try {
|
|
80534
80851
|
const now = new Date;
|
|
80535
|
-
fs28.utimesSync(
|
|
80852
|
+
fs28.utimesSync(path56.join(lock, OWNER), now, now);
|
|
80536
80853
|
} catch {}
|
|
80537
80854
|
};
|
|
80538
80855
|
const beat = setInterval(touch, HEARTBEAT_MS);
|
|
@@ -81383,7 +81700,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
|
|
|
81383
81700
|
return !status.ok || status.stdout.trim() !== "";
|
|
81384
81701
|
}, scratchDir = (cwd) => {
|
|
81385
81702
|
const dir = stacksDir(cwd);
|
|
81386
|
-
return dir.ok ?
|
|
81703
|
+
return dir.ok ? path56.join(dir.data, "worktrees") : null;
|
|
81387
81704
|
}, firstLine = (run) => (`${run.stderr}
|
|
81388
81705
|
${run.stdout}`.split(`
|
|
81389
81706
|
`).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash17("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
|
|
@@ -81398,12 +81715,12 @@ ${run.stdout}`.split(`
|
|
|
81398
81715
|
}, isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), str3 = (value) => typeof value === "string" && value ? value : null, LOCK_WAIT_MS3 = 2000, HEARTBEAT_MS = 20000, HEARTBEAT_STALE_MS, OWNERLESS_STALE_MS = 30000, OWNER = "owner", readOwner = (lock) => {
|
|
81399
81716
|
let raw = null;
|
|
81400
81717
|
try {
|
|
81401
|
-
raw = fs28.readFileSync(
|
|
81718
|
+
raw = fs28.readFileSync(path56.join(lock, OWNER), "utf8");
|
|
81402
81719
|
} catch {
|
|
81403
81720
|
raw = null;
|
|
81404
81721
|
}
|
|
81405
81722
|
let age = null;
|
|
81406
|
-
for (const target of [
|
|
81723
|
+
for (const target of [path56.join(lock, OWNER), lock])
|
|
81407
81724
|
try {
|
|
81408
81725
|
age = Date.now() - fs28.statSync(target).mtimeMs;
|
|
81409
81726
|
break;
|
|
@@ -81446,7 +81763,7 @@ var init_stack = __esm(() => {
|
|
|
81446
81763
|
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
81447
81764
|
import { createHash as createHash18 } from "node:crypto";
|
|
81448
81765
|
import fs29 from "node:fs";
|
|
81449
|
-
import
|
|
81766
|
+
import path57 from "node:path";
|
|
81450
81767
|
function expandBraces2(pattern, limit = 64) {
|
|
81451
81768
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
81452
81769
|
if (pattern[index] === "\\") {
|
|
@@ -81692,7 +82009,7 @@ function parsePlan(raw, defaults, name) {
|
|
|
81692
82009
|
id,
|
|
81693
82010
|
branch,
|
|
81694
82011
|
base: optional("base") ?? "",
|
|
81695
|
-
worktree:
|
|
82012
|
+
worktree: path57.resolve(defaults.root, optional("worktree") ?? path57.join(defaults.worktreeRoot, id)),
|
|
81696
82013
|
tier,
|
|
81697
82014
|
reason,
|
|
81698
82015
|
dependsOn: [...new Set(dependsOn ?? [])],
|
|
@@ -81821,12 +82138,12 @@ function regeneratedFiles(root, tracked) {
|
|
|
81821
82138
|
const found = new Map;
|
|
81822
82139
|
let scripts;
|
|
81823
82140
|
try {
|
|
81824
|
-
const manifest = JSON.parse(fs29.readFileSync(
|
|
82141
|
+
const manifest = JSON.parse(fs29.readFileSync(path57.join(root, "package.json"), "utf8"));
|
|
81825
82142
|
scripts = manifest.scripts ?? {};
|
|
81826
82143
|
} catch {
|
|
81827
82144
|
return found;
|
|
81828
82145
|
}
|
|
81829
|
-
const runner = fs29.existsSync(
|
|
82146
|
+
const runner = fs29.existsSync(path57.join(root, "bun.lock")) ? "bun run" : fs29.existsSync(path57.join(root, "pnpm-lock.yaml")) ? "pnpm run" : fs29.existsSync(path57.join(root, "yarn.lock")) ? "yarn run" : "npm run";
|
|
81830
82147
|
const outputs = tracked.filter((file) => !CODE.test(file));
|
|
81831
82148
|
for (const [name, command] of Object.entries(scripts)) {
|
|
81832
82149
|
if (!name.startsWith("write:") || typeof command !== "string")
|
|
@@ -81834,7 +82151,7 @@ function regeneratedFiles(root, tracked) {
|
|
|
81834
82151
|
for (const file of command.match(/[\w./-]+\.[cm]?[jt]s\b/gu) ?? []) {
|
|
81835
82152
|
let source;
|
|
81836
82153
|
try {
|
|
81837
|
-
source = fs29.readFileSync(
|
|
82154
|
+
source = fs29.readFileSync(path57.join(root, file), "utf8");
|
|
81838
82155
|
} catch {
|
|
81839
82156
|
continue;
|
|
81840
82157
|
}
|
|
@@ -81956,11 +82273,11 @@ function defaultTrunk(cwd) {
|
|
|
81956
82273
|
}
|
|
81957
82274
|
function mainCheckout(cwd) {
|
|
81958
82275
|
const common = gitCommonDir(cwd);
|
|
81959
|
-
return common &&
|
|
82276
|
+
return common && path57.basename(common) === ".git" ? path57.dirname(common) : path57.resolve(cwd);
|
|
81960
82277
|
}
|
|
81961
82278
|
function worktreeRoot(cwd) {
|
|
81962
82279
|
const main = mainCheckout(cwd);
|
|
81963
|
-
return
|
|
82280
|
+
return path57.join(path57.dirname(main), `${path57.basename(main)}-wt`);
|
|
81964
82281
|
}
|
|
81965
82282
|
function sliceHash(slice) {
|
|
81966
82283
|
const {
|
|
@@ -81980,7 +82297,7 @@ function fanoutsDir(cwd) {
|
|
|
81980
82297
|
const root = storeRoot(cwd);
|
|
81981
82298
|
if (!root.ok)
|
|
81982
82299
|
return fanoutFail(root.code, root.error, root.unblock);
|
|
81983
|
-
return { ok: true, data:
|
|
82300
|
+
return { ok: true, data: path57.join(root.value.root, "fanouts") };
|
|
81984
82301
|
}
|
|
81985
82302
|
function parseStored(raw) {
|
|
81986
82303
|
if (!isRecord13(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
|
|
@@ -82048,7 +82365,7 @@ function readFanout(cwd, name) {
|
|
|
82048
82365
|
const dir = fanoutsDir(cwd);
|
|
82049
82366
|
if (!dir.ok)
|
|
82050
82367
|
return dir;
|
|
82051
|
-
const file =
|
|
82368
|
+
const file = path57.join(dir.data, stackFileName(name));
|
|
82052
82369
|
let parsed = null;
|
|
82053
82370
|
try {
|
|
82054
82371
|
parsed = parseStored(JSON.parse(fs29.readFileSync(file, "utf8")));
|
|
@@ -82073,7 +82390,7 @@ function listFanouts(cwd) {
|
|
|
82073
82390
|
for (const entry of names) {
|
|
82074
82391
|
let parsed = null;
|
|
82075
82392
|
try {
|
|
82076
|
-
parsed = parseStored(JSON.parse(fs29.readFileSync(
|
|
82393
|
+
parsed = parseStored(JSON.parse(fs29.readFileSync(path57.join(dir.data, entry), "utf8")));
|
|
82077
82394
|
} catch {
|
|
82078
82395
|
parsed = null;
|
|
82079
82396
|
}
|
|
@@ -82112,7 +82429,7 @@ function writeFanout(cwd, plan) {
|
|
|
82112
82429
|
const dir = fanoutsDir(cwd);
|
|
82113
82430
|
if (!dir.ok)
|
|
82114
82431
|
return dir;
|
|
82115
|
-
const file =
|
|
82432
|
+
const file = path57.join(dir.data, stackFileName(plan.name));
|
|
82116
82433
|
const temp = `${file}.${process.pid}.tmp`;
|
|
82117
82434
|
try {
|
|
82118
82435
|
fs29.mkdirSync(dir.data, { recursive: true });
|
|
@@ -82323,13 +82640,13 @@ var init_fanout = __esm(() => {
|
|
|
82323
82640
|
// packages/workit-core/src/forge/exec.ts
|
|
82324
82641
|
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
82325
82642
|
import fs30 from "node:fs";
|
|
82326
|
-
import
|
|
82643
|
+
import path58 from "node:path";
|
|
82327
82644
|
function findExecutable(bin, env) {
|
|
82328
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
82645
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path58.delimiter).filter(Boolean);
|
|
82329
82646
|
const exts = process.platform === "win32" ? [".exe"] : [""];
|
|
82330
82647
|
for (const dir of dirs)
|
|
82331
82648
|
for (const ext of exts) {
|
|
82332
|
-
const candidate =
|
|
82649
|
+
const candidate = path58.join(dir, `${bin}${ext}`);
|
|
82333
82650
|
try {
|
|
82334
82651
|
const stat = fs30.statSync(candidate);
|
|
82335
82652
|
if (stat.isFile())
|
|
@@ -82699,6 +83016,30 @@ function createGitHubForge(options) {
|
|
|
82699
83016
|
};
|
|
82700
83017
|
return success2(status);
|
|
82701
83018
|
},
|
|
83019
|
+
branchChecks(branch) {
|
|
83020
|
+
const contexts = [];
|
|
83021
|
+
let sha = null;
|
|
83022
|
+
let cursor = null;
|
|
83023
|
+
for (let page = 1;page <= maxPages; page += 1) {
|
|
83024
|
+
const data = graphql(BRANCH_CHECKS_QUERY, { owner, name, ref: `refs/heads/${branch}`, contexts: cursor }, `checks on ${branch}`);
|
|
83025
|
+
if (!data.ok)
|
|
83026
|
+
return data;
|
|
83027
|
+
const commit = data.data.repository?.object ?? null;
|
|
83028
|
+
if (!commit)
|
|
83029
|
+
return failure3("not_found", `branch ${branch} was not found`);
|
|
83030
|
+
sha = commit.oid ?? sha;
|
|
83031
|
+
const rollup = commit.statusCheckRollup;
|
|
83032
|
+
if (!rollup)
|
|
83033
|
+
break;
|
|
83034
|
+
contexts.push(...rollup.contexts.nodes);
|
|
83035
|
+
if (!rollup.contexts.pageInfo.hasNextPage)
|
|
83036
|
+
break;
|
|
83037
|
+
cursor = rollup.contexts.pageInfo.endCursor;
|
|
83038
|
+
}
|
|
83039
|
+
const required = requiredContexts(branch);
|
|
83040
|
+
const checks = latestPerName(contexts.map(checkFromContext).filter((check) => check !== null)).map((check) => required ? { ...check, required: required.includes(check.context) } : check);
|
|
83041
|
+
return success2({ sha, checks, requiredKnown: required !== null });
|
|
83042
|
+
},
|
|
82702
83043
|
jobLogTail(job, lines) {
|
|
82703
83044
|
const text = apiText(runner, "gh", apiHost, ["api", `repos/${repo}/actions/jobs/${job.jobId}/logs`], `job ${job.jobId} log`, FORGE_TIMEOUTS.log);
|
|
82704
83045
|
return text.ok ? success2(logTail(text.data, lines)) : text;
|
|
@@ -82896,7 +83237,7 @@ function createGitHubForge(options) {
|
|
|
82896
83237
|
}
|
|
82897
83238
|
};
|
|
82898
83239
|
}
|
|
82899
|
-
var GITHUB_MAX_PAGES = 10, PR_FIELDS = `number url state isDraft isInMergeQueue mergeable mergeStateStatus baseRefName baseRefOid baseRef { target { oid } } headRefName headRefOid reviewDecision`, THREADS = `reviewThreads(first: 100, after: $threads) { pageInfo { hasNextPage endCursor } nodes { id isResolved isOutdated path line comments(first: 1) { nodes { author { login __typename } body url } } } }`, CONTEXTS = `contexts(first: 100, after: $contexts) { pageInfo { hasNextPage endCursor } nodes { __typename ... on CheckRun { name status conclusion detailsUrl databaseId isRequired(pullRequestNumber: $number) checkSuite { workflowRun { databaseId workflow { name } } } } ... on StatusContext { context state targetUrl isRequired(pullRequestNumber: $number) } } }`, PR_STATUS_QUERY, FIND_PR_QUERY = `query($owner: String!, $name: String!, $head: String!) { repository(owner: $owner, name: $name) { pullRequests(headRefName: $head, first: 20, orderBy: { field: CREATED_AT, direction: DESC }) { nodes { number url state baseRefName headRefName headRefOid headRepositoryOwner { login } } } } }`, READY_MUTATION = `mutation($id: ID!) { markPullRequestReadyForReview(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, DRAFT_MUTATION = `mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, REPLY_MUTATION = `mutation($thread: ID!, $body: String!) { addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $thread, body: $body }) { comment { url } } }`, RESOLVE_MUTATION = `mutation($thread: ID!) { resolveReviewThread(input: { threadId: $thread }) { thread { isResolved } } }`, THREAD_QUERY = `query($thread: ID!) { node(id: $thread) { ... on PullRequestReviewThread { isResolved pullRequest { number } } } }`, prState = (value) => value === "MERGED" ? "merged" : value === "CLOSED" ? "closed" : "open", FAILING2, safeUrl = (value) => value ? redactText(value) : null, encodeRef = (ref) => ref.split("/").map(encodeURIComponent).join("/");
|
|
83240
|
+
var GITHUB_MAX_PAGES = 10, PR_FIELDS = `number url state isDraft isInMergeQueue mergeable mergeStateStatus baseRefName baseRefOid baseRef { target { oid } } headRefName headRefOid reviewDecision`, THREADS = `reviewThreads(first: 100, after: $threads) { pageInfo { hasNextPage endCursor } nodes { id isResolved isOutdated path line comments(first: 1) { nodes { author { login __typename } body url } } } }`, CONTEXTS = `contexts(first: 100, after: $contexts) { pageInfo { hasNextPage endCursor } nodes { __typename ... on CheckRun { name status conclusion detailsUrl databaseId isRequired(pullRequestNumber: $number) checkSuite { workflowRun { databaseId workflow { name } } } } ... on StatusContext { context state targetUrl isRequired(pullRequestNumber: $number) } } }`, PR_STATUS_QUERY, FIND_PR_QUERY = `query($owner: String!, $name: String!, $head: String!) { repository(owner: $owner, name: $name) { pullRequests(headRefName: $head, first: 20, orderBy: { field: CREATED_AT, direction: DESC }) { nodes { number url state baseRefName headRefName headRefOid headRepositoryOwner { login } } } } }`, BRANCH_CHECKS_QUERY = `query($owner: String!, $name: String!, $ref: String!, $contexts: String) { repository(owner: $owner, name: $name) { object(expression: $ref) { ... on Commit { oid statusCheckRollup { contexts(first: 100, after: $contexts) { pageInfo { hasNextPage endCursor } nodes { __typename ... on CheckRun { name status conclusion detailsUrl databaseId checkSuite { workflowRun { databaseId workflow { name } } } } ... on StatusContext { context state targetUrl } } } } } } } }`, READY_MUTATION = `mutation($id: ID!) { markPullRequestReadyForReview(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, DRAFT_MUTATION = `mutation($id: ID!) { convertPullRequestToDraft(input: { pullRequestId: $id }) { pullRequest { isDraft } } }`, REPLY_MUTATION = `mutation($thread: ID!, $body: String!) { addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $thread, body: $body }) { comment { url } } }`, RESOLVE_MUTATION = `mutation($thread: ID!) { resolveReviewThread(input: { threadId: $thread }) { thread { isResolved } } }`, THREAD_QUERY = `query($thread: ID!) { node(id: $thread) { ... on PullRequestReviewThread { isResolved pullRequest { number } } } }`, prState = (value) => value === "MERGED" ? "merged" : value === "CLOSED" ? "closed" : "open", FAILING2, safeUrl = (value) => value ? redactText(value) : null, encodeRef = (ref) => ref.split("/").map(encodeURIComponent).join("/");
|
|
82900
83241
|
var init_github = __esm(() => {
|
|
82901
83242
|
init_exec();
|
|
82902
83243
|
init_redact();
|
|
@@ -83097,6 +83438,35 @@ function createGitLabForge(options) {
|
|
|
83097
83438
|
};
|
|
83098
83439
|
return success2(status);
|
|
83099
83440
|
},
|
|
83441
|
+
branchChecks(branch) {
|
|
83442
|
+
const tip = baseTip(branch);
|
|
83443
|
+
if (!tip)
|
|
83444
|
+
return failure3("not_found", `branch ${branch} was not found`);
|
|
83445
|
+
const latest = apiJson(runner, "glab", apiHost, api(`${project}/pipelines?ref=${encodeURIComponent(branch)}&sha=${tip}&order_by=id&sort=desc&per_page=1`), `pipelines on ${branch}`);
|
|
83446
|
+
if (!latest.ok)
|
|
83447
|
+
return latest;
|
|
83448
|
+
const pipeline = Array.isArray(latest.data) ? latest.data[0] : undefined;
|
|
83449
|
+
if (!pipeline)
|
|
83450
|
+
return success2({ sha: tip, checks: [], requiredKnown: true });
|
|
83451
|
+
const scope = String(pipeline.project_id ?? encodeURIComponent(repo));
|
|
83452
|
+
const jobs = pages(`projects/${scope}/pipelines/${pipeline.id}/jobs`, `pipeline ${pipeline.id} jobs`);
|
|
83453
|
+
if (!jobs.ok)
|
|
83454
|
+
return jobs;
|
|
83455
|
+
const bridges = pages(`projects/${scope}/pipelines/${pipeline.id}/bridges`, `pipeline ${pipeline.id} bridges`);
|
|
83456
|
+
if (!bridges.ok)
|
|
83457
|
+
return bridges;
|
|
83458
|
+
const checks = [
|
|
83459
|
+
...jobs.data.items.map((job) => checkFromJob(job, pipeline.id, scope)),
|
|
83460
|
+
...bridges.data.items.map((bridge) => checkFromBridge(bridge, pipeline.id, scope))
|
|
83461
|
+
];
|
|
83462
|
+
if (stateOf(pipeline.status, false) === "failing" && !checks.some((check) => check.state === "failing"))
|
|
83463
|
+
checks.push(pipelineCheck("failing", pipeline.status, {
|
|
83464
|
+
id: pipeline.id,
|
|
83465
|
+
project_id: pipeline.project_id ?? 0,
|
|
83466
|
+
web_url: pipeline.web_url
|
|
83467
|
+
}));
|
|
83468
|
+
return success2({ sha: tip, checks, requiredKnown: true });
|
|
83469
|
+
},
|
|
83100
83470
|
jobLogTail(job, lines) {
|
|
83101
83471
|
const scope = job.scope ?? encodeURIComponent(repo);
|
|
83102
83472
|
const text = apiText(runner, "glab", apiHost, ["api", `projects/${scope}/jobs/${job.jobId}/trace`], `job ${job.jobId} trace`, FORGE_TIMEOUTS.log);
|
|
@@ -83839,7 +84209,7 @@ var init_live_session = __esm(() => {
|
|
|
83839
84209
|
});
|
|
83840
84210
|
|
|
83841
84211
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
83842
|
-
import
|
|
84212
|
+
import path59 from "node:path";
|
|
83843
84213
|
var CASE_INSENSITIVE, PREFIXES2, SCRIPT_SHELLS, cache3, MUTATING_GIT, DRY_N, MUTATING_WORKIT_GIT, CD2, PUSHD2, POPD2, UNKNOWN_DIR;
|
|
83844
84214
|
var init_write_gate = __esm(() => {
|
|
83845
84215
|
init_task_store();
|
|
@@ -83879,7 +84249,7 @@ var init_write_gate = __esm(() => {
|
|
|
83879
84249
|
CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
83880
84250
|
PUSHD2 = new Set(["pushd", "push-location"]);
|
|
83881
84251
|
POPD2 = new Set(["popd", "pop-location"]);
|
|
83882
|
-
UNKNOWN_DIR =
|
|
84252
|
+
UNKNOWN_DIR = path59.resolve(path59.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
83883
84253
|
});
|
|
83884
84254
|
|
|
83885
84255
|
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
@@ -83904,20 +84274,20 @@ var init_worktree_confine = __esm(() => {
|
|
|
83904
84274
|
|
|
83905
84275
|
// packages/workit-core/src/worktree-deps.ts
|
|
83906
84276
|
import fs31 from "node:fs";
|
|
83907
|
-
import
|
|
84277
|
+
import path60 from "node:path";
|
|
83908
84278
|
function moduleDirs(main) {
|
|
83909
84279
|
const out = [];
|
|
83910
84280
|
const walk = (relative, depth) => {
|
|
83911
84281
|
let entries;
|
|
83912
84282
|
try {
|
|
83913
|
-
entries = fs31.readdirSync(
|
|
84283
|
+
entries = fs31.readdirSync(path60.join(main, relative), { withFileTypes: true });
|
|
83914
84284
|
} catch {
|
|
83915
84285
|
return;
|
|
83916
84286
|
}
|
|
83917
84287
|
for (const entry of entries) {
|
|
83918
84288
|
if (!entry.isDirectory())
|
|
83919
84289
|
continue;
|
|
83920
|
-
const child =
|
|
84290
|
+
const child = path60.join(relative, entry.name);
|
|
83921
84291
|
if (entry.name === MODULES)
|
|
83922
84292
|
out.push(child);
|
|
83923
84293
|
else if (depth < MAX_DEPTH2 && !entry.name.startsWith("."))
|
|
@@ -83931,8 +84301,8 @@ function linkEntry(main, worktree, source, target) {
|
|
|
83931
84301
|
const stat = fs31.lstatSync(source);
|
|
83932
84302
|
if (stat.isSymbolicLink()) {
|
|
83933
84303
|
const link = fs31.readlinkSync(source);
|
|
83934
|
-
const outside =
|
|
83935
|
-
const to = outside ?
|
|
84304
|
+
const outside = path60.isAbsolute(link) && within(link, main) && !path60.relative(main, link).split(path60.sep).includes(MODULES);
|
|
84305
|
+
const to = outside ? path60.join(worktree, path60.relative(main, link)) : link;
|
|
83936
84306
|
fs31.symlinkSync(to, target, process.platform === "win32" ? "junction" : undefined);
|
|
83937
84307
|
return;
|
|
83938
84308
|
}
|
|
@@ -83942,8 +84312,8 @@ function mirror(main, worktree, source, target, top) {
|
|
|
83942
84312
|
fs31.mkdirSync(target);
|
|
83943
84313
|
let count = 0;
|
|
83944
84314
|
for (const entry of fs31.readdirSync(source, { withFileTypes: true })) {
|
|
83945
|
-
const from =
|
|
83946
|
-
const to =
|
|
84315
|
+
const from = path60.join(source, entry.name);
|
|
84316
|
+
const to = path60.join(target, entry.name);
|
|
83947
84317
|
if (top && entry.isDirectory() && (entry.name.startsWith("@") || entry.name === ".bin"))
|
|
83948
84318
|
count += mirror(main, worktree, from, to, false);
|
|
83949
84319
|
else {
|
|
@@ -83959,16 +84329,16 @@ function linkWorktreeDependencies(main, worktree) {
|
|
|
83959
84329
|
const dirs = [];
|
|
83960
84330
|
let entries = 0;
|
|
83961
84331
|
for (const relative of moduleDirs(main)) {
|
|
83962
|
-
const target =
|
|
83963
|
-
if (!fs31.existsSync(
|
|
84332
|
+
const target = path60.join(worktree, relative);
|
|
84333
|
+
if (!fs31.existsSync(path60.dirname(target)) || fs31.existsSync(target))
|
|
83964
84334
|
continue;
|
|
83965
|
-
if (!gitRun4(worktree, ["check-ignore", "-q", `${relative.split(
|
|
83966
|
-
notes.push(`${relative.split(
|
|
84335
|
+
if (!gitRun4(worktree, ["check-ignore", "-q", `${relative.split(path60.sep).join("/")}/`]).ok) {
|
|
84336
|
+
notes.push(`${relative.split(path60.sep).join("/")} is not ignored by git, so it was not linked from ${main}; install dependencies in the worktree yourself`);
|
|
83967
84337
|
continue;
|
|
83968
84338
|
}
|
|
83969
84339
|
try {
|
|
83970
|
-
entries += mirror(main, worktree,
|
|
83971
|
-
dirs.push(relative.split(
|
|
84340
|
+
entries += mirror(main, worktree, path60.join(main, relative), target, true);
|
|
84341
|
+
dirs.push(relative.split(path60.sep).join("/"));
|
|
83972
84342
|
} catch (error) {
|
|
83973
84343
|
fs31.rmSync(target, { recursive: true, force: true });
|
|
83974
84344
|
notes.push(`could not link ${relative} from ${main} (${error.message}); install dependencies in the worktree yourself`);
|
|
@@ -83981,8 +84351,8 @@ function linkWorktreeDependencies(main, worktree) {
|
|
|
83981
84351
|
return { linked: { from: main, dirs, entries, ms }, notes };
|
|
83982
84352
|
}
|
|
83983
84353
|
var MODULES = "node_modules", MAX_DEPTH2 = 2, within = (child, parent) => {
|
|
83984
|
-
const relative =
|
|
83985
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
84354
|
+
const relative = path60.relative(parent, child);
|
|
84355
|
+
return relative === "" || !relative.startsWith("..") && !path60.isAbsolute(relative);
|
|
83986
84356
|
};
|
|
83987
84357
|
var init_worktree_deps = __esm(() => {
|
|
83988
84358
|
init_fanout();
|
|
@@ -83991,7 +84361,7 @@ var init_worktree_deps = __esm(() => {
|
|
|
83991
84361
|
// packages/workit-core/src/fanout-worktree.ts
|
|
83992
84362
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
83993
84363
|
import fs32 from "node:fs";
|
|
83994
|
-
import
|
|
84364
|
+
import path61 from "node:path";
|
|
83995
84365
|
function rowSample(lines) {
|
|
83996
84366
|
const out = [];
|
|
83997
84367
|
let bytes = 0;
|
|
@@ -84051,7 +84421,7 @@ function adminDir(worktree) {
|
|
|
84051
84421
|
const file = dir.stdout.trim();
|
|
84052
84422
|
let token = null;
|
|
84053
84423
|
try {
|
|
84054
|
-
token = fs32.readFileSync(
|
|
84424
|
+
token = fs32.readFileSync(path61.join(file, FANOUT_TOKEN_FILE), "utf8").trim() || null;
|
|
84055
84425
|
} catch {
|
|
84056
84426
|
token = null;
|
|
84057
84427
|
}
|
|
@@ -84059,7 +84429,7 @@ function adminDir(worktree) {
|
|
|
84059
84429
|
const stat = worktreeDeps.stat(file);
|
|
84060
84430
|
return {
|
|
84061
84431
|
path: file,
|
|
84062
|
-
id:
|
|
84432
|
+
id: path61.basename(file),
|
|
84063
84433
|
ino: stat.ino.toString(),
|
|
84064
84434
|
token,
|
|
84065
84435
|
birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
|
|
@@ -84075,7 +84445,7 @@ function markAdminDir(worktree) {
|
|
|
84075
84445
|
return null;
|
|
84076
84446
|
const token = randomBytes5(16).toString("hex");
|
|
84077
84447
|
try {
|
|
84078
|
-
fs32.writeFileSync(
|
|
84448
|
+
fs32.writeFileSync(path61.join(admin.path, FANOUT_TOKEN_FILE), `${token}
|
|
84079
84449
|
`);
|
|
84080
84450
|
} catch {
|
|
84081
84451
|
return null;
|
|
@@ -84090,7 +84460,7 @@ function excludeScratch(cwd) {
|
|
|
84090
84460
|
const common = gitCommonDir(cwd);
|
|
84091
84461
|
if (!common)
|
|
84092
84462
|
return;
|
|
84093
|
-
const file =
|
|
84463
|
+
const file = path61.join(common, "info", "exclude");
|
|
84094
84464
|
let text = "";
|
|
84095
84465
|
try {
|
|
84096
84466
|
text = fs32.readFileSync(file, "utf8");
|
|
@@ -84099,7 +84469,7 @@ function excludeScratch(cwd) {
|
|
|
84099
84469
|
}
|
|
84100
84470
|
if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
|
|
84101
84471
|
return;
|
|
84102
|
-
fs32.mkdirSync(
|
|
84472
|
+
fs32.mkdirSync(path61.dirname(file), { recursive: true });
|
|
84103
84473
|
fs32.appendFileSync(file, `${text && !text.endsWith(`
|
|
84104
84474
|
`) ? `
|
|
84105
84475
|
` : ""}# workit fanout: per-worker scratch dirs
|
|
@@ -84108,13 +84478,13 @@ ${EXCLUDE_LINE}
|
|
|
84108
84478
|
}
|
|
84109
84479
|
function worktreeDependencies(main, worktree) {
|
|
84110
84480
|
for (const [name, install] of LOCKFILES2) {
|
|
84111
|
-
const ours = readBytes(
|
|
84112
|
-
const theirs = readBytes(
|
|
84481
|
+
const ours = readBytes(path61.join(main, name));
|
|
84482
|
+
const theirs = readBytes(path61.join(worktree, name));
|
|
84113
84483
|
if (ours === null && theirs === null)
|
|
84114
84484
|
continue;
|
|
84115
84485
|
if (ours !== null && theirs !== null && ours.equals(theirs))
|
|
84116
84486
|
continue;
|
|
84117
|
-
if (fs32.existsSync(
|
|
84487
|
+
if (fs32.existsSync(path61.join(worktree, "node_modules")))
|
|
84118
84488
|
break;
|
|
84119
84489
|
const command = install(worktree);
|
|
84120
84490
|
return {
|
|
@@ -84128,7 +84498,7 @@ function worktreeDependencies(main, worktree) {
|
|
|
84128
84498
|
return { ...linkWorktreeDependencies(main, worktree), install: null };
|
|
84129
84499
|
}
|
|
84130
84500
|
function ensureScratch(worktree) {
|
|
84131
|
-
const scratch =
|
|
84501
|
+
const scratch = path61.join(worktree, SCRATCH_DIR);
|
|
84132
84502
|
fs32.mkdirSync(scratch, { recursive: true });
|
|
84133
84503
|
return scratch;
|
|
84134
84504
|
}
|
|
@@ -84367,7 +84737,7 @@ function unpushedCount(cwd, branch) {
|
|
|
84367
84737
|
return count.ok ? Number(count.stdout.trim()) : null;
|
|
84368
84738
|
}
|
|
84369
84739
|
function removeScratch(worktree) {
|
|
84370
|
-
const scratch =
|
|
84740
|
+
const scratch = path61.join(worktree, SCRATCH_DIR);
|
|
84371
84741
|
let stat;
|
|
84372
84742
|
try {
|
|
84373
84743
|
stat = fs32.lstatSync(scratch);
|
|
@@ -84383,16 +84753,16 @@ var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000,
|
|
|
84383
84753
|
try {
|
|
84384
84754
|
return fs32.realpathSync.native(target);
|
|
84385
84755
|
} catch {
|
|
84386
|
-
return
|
|
84756
|
+
return path61.resolve(target);
|
|
84387
84757
|
}
|
|
84388
84758
|
}, samePath = (a, b) => {
|
|
84389
84759
|
const left = canonical2(a);
|
|
84390
84760
|
const right = canonical2(b);
|
|
84391
84761
|
return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
84392
84762
|
}, inside3 = (child, parent) => {
|
|
84393
|
-
const relative =
|
|
84394
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
84395
|
-
}, slicePath = (cwd, slice) =>
|
|
84763
|
+
const relative = path61.relative(canonical2(parent), canonical2(child));
|
|
84764
|
+
return relative === "" || !relative.startsWith("..") && !path61.isAbsolute(relative);
|
|
84765
|
+
}, slicePath = (cwd, slice) => path61.isAbsolute(slice.worktree) ? slice.worktree : path61.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, worktreeDeps, LOCKFILES2, readBytes = (file) => {
|
|
84396
84766
|
try {
|
|
84397
84767
|
return fs32.readFileSync(file);
|
|
84398
84768
|
} catch {
|
|
@@ -84423,7 +84793,7 @@ var init_fanout_worktree = __esm(() => {
|
|
|
84423
84793
|
|
|
84424
84794
|
// packages/workit-core/src/forge/merge-cleanup.ts
|
|
84425
84795
|
import fs33 from "node:fs";
|
|
84426
|
-
import
|
|
84796
|
+
import path62 from "node:path";
|
|
84427
84797
|
function cleanupMergedBranch(cwd, branch, mergedHead) {
|
|
84428
84798
|
const out = { branchDeleted: false, worktreeRemoved: null, notes: [] };
|
|
84429
84799
|
if (!branch || branch.startsWith("-"))
|
|
@@ -84483,13 +84853,13 @@ var SLOW2, isFanoutWorktree = (worktree) => {
|
|
|
84483
84853
|
if (!dir.ok)
|
|
84484
84854
|
return false;
|
|
84485
84855
|
try {
|
|
84486
|
-
return fs33.readFileSync(
|
|
84856
|
+
return fs33.readFileSync(path62.join(dir.stdout.trim(), FANOUT_TOKEN_FILE), "utf8").trim() !== "";
|
|
84487
84857
|
} catch {
|
|
84488
84858
|
return false;
|
|
84489
84859
|
}
|
|
84490
84860
|
}, inside4 = (child, parent) => {
|
|
84491
|
-
const relative =
|
|
84492
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
84861
|
+
const relative = path62.relative(fs33.realpathSync.native(parent), fs33.realpathSync.native(child));
|
|
84862
|
+
return relative === "" || !relative.startsWith("..") && !path62.isAbsolute(relative);
|
|
84493
84863
|
};
|
|
84494
84864
|
var init_merge_cleanup = __esm(() => {
|
|
84495
84865
|
init_fanout();
|
|
@@ -84770,7 +85140,7 @@ var init_duration = __esm(() => {
|
|
|
84770
85140
|
});
|
|
84771
85141
|
|
|
84772
85142
|
// packages/workit-cli/src/verbs/forge-common.ts
|
|
84773
|
-
import { readFileSync as
|
|
85143
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
84774
85144
|
function parseFlags(argv, spec) {
|
|
84775
85145
|
const parsed = { values: {}, booleans: new Set, lists: {}, positionals: [] };
|
|
84776
85146
|
for (let index = 0;index < argv.length; index += 1) {
|
|
@@ -84925,7 +85295,7 @@ var init_forge_common = __esm(() => {
|
|
|
84925
85295
|
forgeDeps = {
|
|
84926
85296
|
runner: undefined,
|
|
84927
85297
|
npm: undefined,
|
|
84928
|
-
readStdin: () =>
|
|
85298
|
+
readStdin: () => readFileSync24(0, "utf8"),
|
|
84929
85299
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
84930
85300
|
now: () => Date.now()
|
|
84931
85301
|
};
|
|
@@ -84936,12 +85306,12 @@ var exports_pr = {};
|
|
|
84936
85306
|
__export(exports_pr, {
|
|
84937
85307
|
run: () => run15
|
|
84938
85308
|
});
|
|
84939
|
-
import { readFileSync as
|
|
84940
|
-
import
|
|
85309
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
85310
|
+
import path63 from "node:path";
|
|
84941
85311
|
function readBodyFile(io, file) {
|
|
84942
85312
|
try {
|
|
84943
85313
|
return {
|
|
84944
|
-
body: file === "-" ? forgeDeps.readStdin() :
|
|
85314
|
+
body: file === "-" ? forgeDeps.readStdin() : readFileSync25(path63.resolve(io.cwd, file), "utf8")
|
|
84945
85315
|
};
|
|
84946
85316
|
} catch {
|
|
84947
85317
|
return `cannot read --body-file ${file}`;
|
|
@@ -85204,6 +85574,7 @@ async function merge3(argv, io) {
|
|
|
85204
85574
|
"delete-branch": "boolean",
|
|
85205
85575
|
"keep-branch": "boolean",
|
|
85206
85576
|
unverified: "boolean",
|
|
85577
|
+
"base-red-ok": "boolean",
|
|
85207
85578
|
reason: "value",
|
|
85208
85579
|
subject: "value",
|
|
85209
85580
|
body: "value",
|
|
@@ -85211,7 +85582,11 @@ async function merge3(argv, io) {
|
|
|
85211
85582
|
});
|
|
85212
85583
|
if (typeof flags === "string")
|
|
85213
85584
|
return usage4(io, flags, MERGE_USAGE);
|
|
85214
|
-
const
|
|
85585
|
+
const baseRedOk = flags.booleans.has("base-red-ok");
|
|
85586
|
+
const baseRedReason = flags.values.reason?.trim();
|
|
85587
|
+
if (baseRedOk && !baseRedReason)
|
|
85588
|
+
return usage4(io, "--base-red-ok needs --reason <why merging onto a red base is fine>", MERGE_USAGE);
|
|
85589
|
+
const unverified = baseRedOk && !flags.booleans.has("unverified") ? null : unverifiedFlag(flags);
|
|
85215
85590
|
if (typeof unverified === "string")
|
|
85216
85591
|
return usage4(io, unverified, MERGE_USAGE);
|
|
85217
85592
|
if (flags.positionals.length)
|
|
@@ -85253,6 +85628,7 @@ async function merge3(argv, io) {
|
|
|
85253
85628
|
implicitDelete: !flags.booleans.has("keep-branch") && workspaceDeleteBranch(io.cwd),
|
|
85254
85629
|
actor: actorFromEnv(io.env),
|
|
85255
85630
|
...unverified ? { unverified } : {},
|
|
85631
|
+
...baseRedOk && baseRedReason ? { baseRedOk: { reason: baseRedReason } } : {},
|
|
85256
85632
|
...subject !== undefined ? { subject } : {},
|
|
85257
85633
|
...body !== undefined ? { body } : {}
|
|
85258
85634
|
}, forgeDeps.sleep);
|
|
@@ -85265,6 +85641,7 @@ async function merge3(argv, io) {
|
|
|
85265
85641
|
return emit(io, ok2({ ...result.data, localCleanup: cleanup }), (data) => [
|
|
85266
85642
|
`merged ${connected.data.forge.kind === "github" ? "PR #" : "MR !"}${data.number} (${data.method}) at head ${data.head.slice(0, 12)}${data.mergeSha ? ` -> ${data.mergeSha.slice(0, 12)}` : ""}`,
|
|
85267
85643
|
data.unverified ? `UNVERIFIED: merged without a verdict (--unverified: ${data.unverified.reason}); ledger ${data.unverified.recorded}` : `verdict ${data.verdict.verdictId ?? "?"} accepted for that head${data.verdict.cover ? ` (stack verdict on ${data.verdict.cover.branch} @ ${data.verdict.cover.head.slice(0, 12)})` : data.verdict.carry ? ` (${carryLabel(data.verdict.carry)})` : ""}`,
|
|
85644
|
+
...data.baseCi.note ? [data.baseCi.note] : [],
|
|
85268
85645
|
...data.mergeBack.length ? [
|
|
85269
85646
|
`merge back: ${data.base} is the ${data.mergeBackTrack} production branch; bring it into ${data.mergeBack.join(", ")}`
|
|
85270
85647
|
] : [],
|
|
@@ -85431,7 +85808,7 @@ async function run15(argv, io) {
|
|
|
85431
85808
|
return reply(rest, io);
|
|
85432
85809
|
return usage4(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE8);
|
|
85433
85810
|
}
|
|
85434
|
-
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--issue <#n|ID|url>]… [--refs] [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE8 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, NEED_TEXT, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : landed.lane === "none" && !landed.verdictId ? `merged at ${(landed.head ?? "?").slice(0, 12)} with no verdict (lane none)` : landed.review === "self-reviewed" ? `merged self-reviewed (lane self) with the author's verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}${landed.intentOnly ? " (merge intent recorded; the merge call itself failed)" : ""}`, acceptedHow = (verdict) => verdict.cover ? `stack verdict ${verdict.cover.verdictId} on ${verdict.cover.branch} @ ${verdict.cover.head.slice(0, 12)}` : verdict.carry ? carryLabel(verdict.carry) : verdict.basis, METHODS2;
|
|
85811
|
+
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--issue <#n|ID|url>]… [--refs] [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE8 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, NEED_TEXT, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : landed.lane === "none" && !landed.verdictId ? `merged at ${(landed.head ?? "?").slice(0, 12)} with no verdict (lane none)` : landed.review === "self-reviewed" ? `merged self-reviewed (lane self) with the author's verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}${landed.intentOnly ? " (merge intent recorded; the merge call itself failed)" : ""}`, acceptedHow = (verdict) => verdict.cover ? `stack verdict ${verdict.cover.verdictId} on ${verdict.cover.branch} @ ${verdict.cover.head.slice(0, 12)}` : verdict.carry ? carryLabel(verdict.carry) : verdict.basis, METHODS2;
|
|
85435
85812
|
var init_pr = __esm(() => {
|
|
85436
85813
|
init_autonomy();
|
|
85437
85814
|
init_issue_link();
|
|
@@ -85730,8 +86107,8 @@ var exports_git = {};
|
|
|
85730
86107
|
__export(exports_git, {
|
|
85731
86108
|
run: () => run17
|
|
85732
86109
|
});
|
|
85733
|
-
import { readFileSync as
|
|
85734
|
-
import
|
|
86110
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
86111
|
+
import path64 from "node:path";
|
|
85735
86112
|
function kindHint(cwd, kind) {
|
|
85736
86113
|
let kinds = [...BRANCH_KINDS];
|
|
85737
86114
|
try {
|
|
@@ -85835,7 +86212,7 @@ function commitMessage(parsed, io) {
|
|
|
85835
86212
|
|
|
85836
86213
|
`);
|
|
85837
86214
|
try {
|
|
85838
|
-
return file === "-" ?
|
|
86215
|
+
return file === "-" ? readFileSync26(0, "utf8") : readFileSync26(path64.resolve(io.cwd, file), "utf8");
|
|
85839
86216
|
} catch (error) {
|
|
85840
86217
|
return {
|
|
85841
86218
|
error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -87245,7 +87622,7 @@ var init_stack2 = __esm(() => {
|
|
|
87245
87622
|
// packages/workit-core/src/fanout-brief.ts
|
|
87246
87623
|
import { createHash as createHash19 } from "node:crypto";
|
|
87247
87624
|
import fs35 from "node:fs";
|
|
87248
|
-
import
|
|
87625
|
+
import path65 from "node:path";
|
|
87249
87626
|
function workerSession(lead, slice, attempt) {
|
|
87250
87627
|
const tail = attempt === null ? "" : `+try${attempt}`;
|
|
87251
87628
|
const full = `${lead}-w-${slice}${tail}`;
|
|
@@ -87327,7 +87704,7 @@ function renderBrief(cwd, plan, input) {
|
|
|
87327
87704
|
"STANDING:",
|
|
87328
87705
|
...standing.length ? standing.map((order) => ` - ${order.what}`) : [" - none recorded beyond this brief"],
|
|
87329
87706
|
exists ? `WORKTREE: ${planned} (made by \`workit fanout worktree create ${slice.id}\`; yours alone)` : `WORKTREE: ${planned} (not made yet: the lead runs \`workit fanout worktree create ${slice.id}\` before spawning, then renders this brief again)`,
|
|
87330
|
-
...exists && typeof made?.install === "string" && !fs35.existsSync(
|
|
87707
|
+
...exists && typeof made?.install === "string" && !fs35.existsSync(path65.join(planned, "node_modules")) ? [`INSTALL: \`${made.install}\` first: the main checkout's node_modules was not linked here`] : [],
|
|
87331
87708
|
...identityLines(identity, minted),
|
|
87332
87709
|
...rules(mode, branch, slice.base, exists ? planned : null)
|
|
87333
87710
|
];
|
|
@@ -88068,7 +88445,7 @@ __export(exports_fanout, {
|
|
|
88068
88445
|
run: () => run21
|
|
88069
88446
|
});
|
|
88070
88447
|
import fs36 from "node:fs";
|
|
88071
|
-
import
|
|
88448
|
+
import path66 from "node:path";
|
|
88072
88449
|
function forgeFor(io, offline) {
|
|
88073
88450
|
if (offline)
|
|
88074
88451
|
return { forge: null, error: "--offline" };
|
|
@@ -88129,7 +88506,7 @@ function plan2(argv, io) {
|
|
|
88129
88506
|
return usage4(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
|
|
88130
88507
|
if (flags.positionals.length !== 1)
|
|
88131
88508
|
return usage4(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
|
|
88132
|
-
const file =
|
|
88509
|
+
const file = path66.resolve(io.cwd, flags.positionals[0]);
|
|
88133
88510
|
let raw;
|
|
88134
88511
|
try {
|
|
88135
88512
|
raw = JSON.parse(fs36.readFileSync(file, "utf8"));
|
|
@@ -103670,7 +104047,7 @@ var init_ast = __esm(() => {
|
|
|
103670
104047
|
|
|
103671
104048
|
// packages/workit-cli/src/test-audit/rules.ts
|
|
103672
104049
|
import { builtinModules } from "node:module";
|
|
103673
|
-
import
|
|
104050
|
+
import path67 from "node:path";
|
|
103674
104051
|
function testCallee(call) {
|
|
103675
104052
|
let callee = call.callee;
|
|
103676
104053
|
if (callee.type === "CallExpression")
|
|
@@ -104163,12 +104540,12 @@ function checkConstantsOnly(ctx, emit) {
|
|
|
104163
104540
|
}
|
|
104164
104541
|
function checkOverMocking(ctx, emit) {
|
|
104165
104542
|
const { program } = ctx.parsed;
|
|
104166
|
-
const unit =
|
|
104543
|
+
const unit = path67.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
|
|
104167
104544
|
const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
|
|
104168
104545
|
const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
|
|
104169
104546
|
for (const call of mocked) {
|
|
104170
104547
|
const spec = call.arguments[0].value;
|
|
104171
|
-
const base =
|
|
104548
|
+
const base = path67.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
104172
104549
|
if (base === unit)
|
|
104173
104550
|
emit({
|
|
104174
104551
|
rule: "over-mocking",
|
|
@@ -104557,8 +104934,8 @@ var init_rules = __esm(() => {
|
|
|
104557
104934
|
|
|
104558
104935
|
// packages/workit-cli/src/test-audit/audit.ts
|
|
104559
104936
|
import { spawnSync as spawnSync29 } from "node:child_process";
|
|
104560
|
-
import { existsSync as existsSync24, readdirSync as
|
|
104561
|
-
import
|
|
104937
|
+
import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync27, statSync as statSync10 } from "node:fs";
|
|
104938
|
+
import path68 from "node:path";
|
|
104562
104939
|
function git7(cwd, args) {
|
|
104563
104940
|
const run = spawnSync29("git", ["-c", "core.quotepath=off", ...args], {
|
|
104564
104941
|
cwd,
|
|
@@ -104568,10 +104945,10 @@ function git7(cwd, args) {
|
|
|
104568
104945
|
return { ok: run.status === 0, stdout: run.stdout ?? "" };
|
|
104569
104946
|
}
|
|
104570
104947
|
function walkTests(dir, out) {
|
|
104571
|
-
for (const entry of
|
|
104948
|
+
for (const entry of readdirSync13(dir, { withFileTypes: true })) {
|
|
104572
104949
|
if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
|
|
104573
104950
|
continue;
|
|
104574
|
-
const full =
|
|
104951
|
+
const full = path68.join(dir, entry.name);
|
|
104575
104952
|
if (entry.isDirectory()) {
|
|
104576
104953
|
if (!SKIP_DIRS.has(entry.name))
|
|
104577
104954
|
walkTests(full, out);
|
|
@@ -104583,7 +104960,7 @@ function expandPaths(cwd, inputs) {
|
|
|
104583
104960
|
const files = [];
|
|
104584
104961
|
const missing = [];
|
|
104585
104962
|
for (const input of inputs.length > 0 ? inputs : ["."]) {
|
|
104586
|
-
const full =
|
|
104963
|
+
const full = path68.resolve(cwd, input);
|
|
104587
104964
|
if (!existsSync24(full))
|
|
104588
104965
|
missing.push(input);
|
|
104589
104966
|
else if (statSync10(full).isDirectory())
|
|
@@ -104620,11 +104997,11 @@ function changedLines(cwd, base) {
|
|
|
104620
104997
|
for (let offset = 0;offset < count; offset += 1)
|
|
104621
104998
|
lines.add(start + offset);
|
|
104622
104999
|
}
|
|
104623
|
-
changed.set(
|
|
105000
|
+
changed.set(path68.join(root, file), lines);
|
|
104624
105001
|
}
|
|
104625
105002
|
const untracked = git7(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
|
|
104626
105003
|
for (const file of untracked.stdout.split("\x00").filter(Boolean))
|
|
104627
|
-
changed.set(
|
|
105004
|
+
changed.set(path68.join(root, file), "all");
|
|
104628
105005
|
return changed;
|
|
104629
105006
|
}
|
|
104630
105007
|
function auditFiles(files, display) {
|
|
@@ -104637,7 +105014,7 @@ function auditFiles(files, display) {
|
|
|
104637
105014
|
const shown = display(file);
|
|
104638
105015
|
let parsed;
|
|
104639
105016
|
try {
|
|
104640
|
-
parsed = parseSource(
|
|
105017
|
+
parsed = parseSource(readFileSync27(file, "utf8"), file);
|
|
104641
105018
|
} catch (error) {
|
|
104642
105019
|
parseErrors.push({
|
|
104643
105020
|
file: shown,
|
|
@@ -104686,7 +105063,7 @@ function auditFiles(files, display) {
|
|
|
104686
105063
|
durationMs: Math.round(performance.now() - started)
|
|
104687
105064
|
};
|
|
104688
105065
|
}
|
|
104689
|
-
var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(
|
|
105066
|
+
var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path68.sep).join("/"), SEVERITY_ORDER, ruleIds;
|
|
104690
105067
|
var init_audit = __esm(() => {
|
|
104691
105068
|
init_ast();
|
|
104692
105069
|
init_rules();
|
|
@@ -104702,21 +105079,21 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
104702
105079
|
import {
|
|
104703
105080
|
existsSync as existsSync25,
|
|
104704
105081
|
lstatSync as lstatSync8,
|
|
104705
|
-
mkdirSync as
|
|
105082
|
+
mkdirSync as mkdirSync12,
|
|
104706
105083
|
mkdtempSync as mkdtempSync4,
|
|
104707
|
-
readdirSync as
|
|
104708
|
-
readFileSync as
|
|
105084
|
+
readdirSync as readdirSync14,
|
|
105085
|
+
readFileSync as readFileSync28,
|
|
104709
105086
|
readlinkSync as readlinkSync3,
|
|
104710
105087
|
realpathSync as realpathSync10,
|
|
104711
105088
|
renameSync as renameSync7,
|
|
104712
105089
|
rmSync as rmSync9,
|
|
104713
105090
|
statSync as statSync11,
|
|
104714
105091
|
symlinkSync,
|
|
104715
|
-
writeFileSync as
|
|
105092
|
+
writeFileSync as writeFileSync12,
|
|
104716
105093
|
copyFileSync as copyFileSync8
|
|
104717
105094
|
} from "node:fs";
|
|
104718
105095
|
import os22 from "node:os";
|
|
104719
|
-
import
|
|
105096
|
+
import path69 from "node:path";
|
|
104720
105097
|
function mutantsFor(source, file, lines) {
|
|
104721
105098
|
const parsed = parseSource(source, file);
|
|
104722
105099
|
const out = [];
|
|
@@ -104789,7 +105166,7 @@ function relatedTests(source, testFiles) {
|
|
|
104789
105166
|
for (const match of text.matchAll(SPECIFIER)) {
|
|
104790
105167
|
const spec = match[1];
|
|
104791
105168
|
if (spec.startsWith(".")) {
|
|
104792
|
-
if (stripExt(
|
|
105169
|
+
if (stripExt(path69.posix.join(path69.posix.dirname(file), spec)) === target)
|
|
104793
105170
|
return true;
|
|
104794
105171
|
} else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
|
|
104795
105172
|
return true;
|
|
@@ -104801,7 +105178,7 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
|
|
|
104801
105178
|
let removed = 0;
|
|
104802
105179
|
let names = [];
|
|
104803
105180
|
try {
|
|
104804
|
-
names =
|
|
105181
|
+
names = readdirSync14(tmp);
|
|
104805
105182
|
} catch {
|
|
104806
105183
|
return 0;
|
|
104807
105184
|
}
|
|
@@ -104809,12 +105186,12 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
|
|
|
104809
105186
|
for (const name of names) {
|
|
104810
105187
|
if (!name.startsWith(TEMP_PREFIX))
|
|
104811
105188
|
continue;
|
|
104812
|
-
const dir =
|
|
105189
|
+
const dir = path69.join(tmp, name);
|
|
104813
105190
|
try {
|
|
104814
105191
|
const stat = lstatSync8(dir);
|
|
104815
105192
|
if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
|
|
104816
105193
|
continue;
|
|
104817
|
-
const pid = Number(
|
|
105194
|
+
const pid = Number(readFileSync28(path69.join(dir, PID_FILE), "utf8"));
|
|
104818
105195
|
if (Number.isInteger(pid) && pid > 0 && alive(pid))
|
|
104819
105196
|
continue;
|
|
104820
105197
|
} catch (error) {
|
|
@@ -104829,16 +105206,16 @@ function removeStaleCopies(tmp = os22.tmpdir()) {
|
|
|
104829
105206
|
return removed;
|
|
104830
105207
|
}
|
|
104831
105208
|
function linkNodeModules(origin, target, repo, copy) {
|
|
104832
|
-
|
|
104833
|
-
for (const entry of
|
|
104834
|
-
const from =
|
|
104835
|
-
const to =
|
|
105209
|
+
mkdirSync12(target, { recursive: true });
|
|
105210
|
+
for (const entry of readdirSync14(origin, { withFileTypes: true })) {
|
|
105211
|
+
const from = path69.join(origin, entry.name);
|
|
105212
|
+
const to = path69.join(target, entry.name);
|
|
104836
105213
|
if (entry.name.startsWith("@") && entry.isDirectory()) {
|
|
104837
105214
|
linkNodeModules(from, to, repo, copy);
|
|
104838
105215
|
continue;
|
|
104839
105216
|
}
|
|
104840
105217
|
if (entry.name.startsWith(".") && entry.name !== ".bin" && entry.isDirectory()) {
|
|
104841
|
-
|
|
105218
|
+
mkdirSync12(to, { recursive: true });
|
|
104842
105219
|
continue;
|
|
104843
105220
|
}
|
|
104844
105221
|
if (entry.isSymbolicLink()) {
|
|
@@ -104848,9 +105225,9 @@ function linkNodeModules(origin, target, repo, copy) {
|
|
|
104848
105225
|
} catch {
|
|
104849
105226
|
continue;
|
|
104850
105227
|
}
|
|
104851
|
-
const relative =
|
|
104852
|
-
if (!relative.startsWith("..") && !
|
|
104853
|
-
symlinkSync(
|
|
105228
|
+
const relative = path69.relative(repo, real);
|
|
105229
|
+
if (!relative.startsWith("..") && !path69.isAbsolute(relative) && !relative.split(path69.sep).includes("node_modules")) {
|
|
105230
|
+
symlinkSync(path69.join(copy, relative), to, "junction");
|
|
104854
105231
|
continue;
|
|
104855
105232
|
}
|
|
104856
105233
|
}
|
|
@@ -104866,7 +105243,7 @@ function copySymlink(repo, tree, from, to) {
|
|
|
104866
105243
|
}
|
|
104867
105244
|
const realRepo = realpathSync10(repo);
|
|
104868
105245
|
if (real && inside5(realRepo, real)) {
|
|
104869
|
-
symlinkSync(
|
|
105246
|
+
symlinkSync(path69.relative(path69.dirname(to), path69.join(tree, path69.relative(realRepo, real))), to);
|
|
104870
105247
|
return;
|
|
104871
105248
|
}
|
|
104872
105249
|
if (real && statSync11(real).isFile())
|
|
@@ -104875,23 +105252,23 @@ function copySymlink(repo, tree, from, to) {
|
|
|
104875
105252
|
symlinkSync(readlinkSync3(from), to);
|
|
104876
105253
|
}
|
|
104877
105254
|
function writeInside(copy, file, content) {
|
|
104878
|
-
const target =
|
|
105255
|
+
const target = path69.join(copy, file);
|
|
104879
105256
|
try {
|
|
104880
105257
|
const stat = lstatSync8(target);
|
|
104881
|
-
if (!stat.isFile() || !inside5(realpathSync10(copy), realpathSync10(
|
|
105258
|
+
if (!stat.isFile() || !inside5(realpathSync10(copy), realpathSync10(path69.dirname(target))))
|
|
104882
105259
|
return false;
|
|
104883
105260
|
} catch {
|
|
104884
105261
|
return false;
|
|
104885
105262
|
}
|
|
104886
105263
|
const temp = `${target}.workit-mutant-${process.pid}`;
|
|
104887
|
-
|
|
105264
|
+
writeFileSync12(temp, content);
|
|
104888
105265
|
renameSync7(temp, target);
|
|
104889
105266
|
return true;
|
|
104890
105267
|
}
|
|
104891
105268
|
function copyWorkingTree(repo) {
|
|
104892
|
-
const dir = mkdtempSync4(
|
|
104893
|
-
|
|
104894
|
-
const tree =
|
|
105269
|
+
const dir = mkdtempSync4(path69.join(os22.tmpdir(), TEMP_PREFIX));
|
|
105270
|
+
writeFileSync12(path69.join(dir, PID_FILE), String(process.pid));
|
|
105271
|
+
const tree = path69.join(dir, "tree");
|
|
104895
105272
|
const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
|
|
104896
105273
|
if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
|
|
104897
105274
|
const branch = git7(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
|
|
@@ -104902,35 +105279,35 @@ function copyWorkingTree(repo) {
|
|
|
104902
105279
|
git7(tree, ["update-ref", "--no-deref", "HEAD", head]);
|
|
104903
105280
|
git7(tree, ["reset", "-q"]);
|
|
104904
105281
|
} else
|
|
104905
|
-
|
|
105282
|
+
mkdirSync12(tree, { recursive: true });
|
|
104906
105283
|
const listed = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00");
|
|
104907
105284
|
const dirs = new Set([""]);
|
|
104908
105285
|
for (const relative of listed) {
|
|
104909
105286
|
if (!relative)
|
|
104910
105287
|
continue;
|
|
104911
|
-
const from =
|
|
105288
|
+
const from = path69.join(repo, relative);
|
|
104912
105289
|
let stat;
|
|
104913
105290
|
try {
|
|
104914
105291
|
stat = lstatSync8(from);
|
|
104915
105292
|
} catch {
|
|
104916
105293
|
continue;
|
|
104917
105294
|
}
|
|
104918
|
-
const to =
|
|
104919
|
-
|
|
105295
|
+
const to = path69.join(tree, relative);
|
|
105296
|
+
mkdirSync12(path69.dirname(to), { recursive: true });
|
|
104920
105297
|
if (stat.isSymbolicLink())
|
|
104921
105298
|
copySymlink(repo, tree, from, to);
|
|
104922
105299
|
else if (stat.isFile())
|
|
104923
105300
|
copyFileSync8(from, to);
|
|
104924
|
-
let parent =
|
|
105301
|
+
let parent = path69.dirname(relative);
|
|
104925
105302
|
while (parent !== "." && !dirs.has(parent)) {
|
|
104926
105303
|
dirs.add(parent);
|
|
104927
|
-
parent =
|
|
105304
|
+
parent = path69.dirname(parent);
|
|
104928
105305
|
}
|
|
104929
105306
|
}
|
|
104930
105307
|
for (const parent of dirs) {
|
|
104931
|
-
const modules =
|
|
104932
|
-
if (existsSync25(modules) && !existsSync25(
|
|
104933
|
-
linkNodeModules(modules,
|
|
105308
|
+
const modules = path69.join(repo, parent, "node_modules");
|
|
105309
|
+
if (existsSync25(modules) && !existsSync25(path69.join(tree, parent, "node_modules")))
|
|
105310
|
+
linkNodeModules(modules, path69.join(tree, parent, "node_modules"), repo, tree);
|
|
104934
105311
|
}
|
|
104935
105312
|
return { dir, tree };
|
|
104936
105313
|
}
|
|
@@ -104969,20 +105346,20 @@ async function runMutation(options) {
|
|
|
104969
105346
|
const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
|
|
104970
105347
|
const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
|
|
104971
105348
|
try {
|
|
104972
|
-
return { file, text:
|
|
105349
|
+
return { file, text: readFileSync28(path69.join(repo, file), "utf8") };
|
|
104973
105350
|
} catch {
|
|
104974
105351
|
return { file, text: "" };
|
|
104975
105352
|
}
|
|
104976
105353
|
});
|
|
104977
105354
|
const candidates = [];
|
|
104978
105355
|
for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
104979
|
-
const relative = toPosix(
|
|
105356
|
+
const relative = toPosix(path69.relative(repo, absolute));
|
|
104980
105357
|
if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
|
|
104981
105358
|
continue;
|
|
104982
105359
|
if (NOT_SOURCE.test(relative) || !existsSync25(absolute))
|
|
104983
105360
|
continue;
|
|
104984
105361
|
try {
|
|
104985
|
-
candidates.push(...mutantsFor(
|
|
105362
|
+
candidates.push(...mutantsFor(readFileSync28(absolute, "utf8"), relative, lines));
|
|
104986
105363
|
} catch {}
|
|
104987
105364
|
}
|
|
104988
105365
|
const mutants = candidates.map((mutant, index) => ({ ...mutant, id: index + 1 }));
|
|
@@ -104990,9 +105367,9 @@ async function runMutation(options) {
|
|
|
104990
105367
|
for (const mutant of mutants)
|
|
104991
105368
|
if (!related.has(mutant.file))
|
|
104992
105369
|
related.set(mutant.file, relatedTests(mutant.file, testFiles));
|
|
104993
|
-
const cwdRelative =
|
|
105370
|
+
const cwdRelative = path69.relative(repo, options.cwd);
|
|
104994
105371
|
const asArg = (file) => {
|
|
104995
|
-
const relative = toPosix(
|
|
105372
|
+
const relative = toPosix(path69.relative(cwdRelative, file));
|
|
104996
105373
|
return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
|
|
104997
105374
|
};
|
|
104998
105375
|
const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
|
|
@@ -105028,7 +105405,7 @@ async function runMutation(options) {
|
|
|
105028
105405
|
return finish3(options, 0, results, counts, started);
|
|
105029
105406
|
removeStaleCopies();
|
|
105030
105407
|
const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
|
|
105031
|
-
const copyCwd =
|
|
105408
|
+
const copyCwd = path69.join(copy, cwdRelative);
|
|
105032
105409
|
let current = null;
|
|
105033
105410
|
const restore = () => {
|
|
105034
105411
|
if (current)
|
|
@@ -105063,7 +105440,7 @@ async function runMutation(options) {
|
|
|
105063
105440
|
record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
|
|
105064
105441
|
continue;
|
|
105065
105442
|
}
|
|
105066
|
-
const original =
|
|
105443
|
+
const original = readFileSync28(path69.join(copy, mutant.file), "utf8");
|
|
105067
105444
|
const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
|
|
105068
105445
|
current = { file: mutant.file, original };
|
|
105069
105446
|
if (!writeInside(copy, mutant.file, mutated)) {
|
|
@@ -105120,8 +105497,8 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
|
|
|
105120
105497
|
return error.code === "EPERM";
|
|
105121
105498
|
}
|
|
105122
105499
|
}, inside5 = (root, file) => {
|
|
105123
|
-
const relative =
|
|
105124
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
105500
|
+
const relative = path69.relative(root, file);
|
|
105501
|
+
return relative === "" || !relative.startsWith("..") && !path69.isAbsolute(relative);
|
|
105125
105502
|
}, active, killTree = (child) => {
|
|
105126
105503
|
try {
|
|
105127
105504
|
if (child.pid && process.platform !== "win32")
|
|
@@ -105170,7 +105547,7 @@ __export(exports_test_audit, {
|
|
|
105170
105547
|
run: () => run23
|
|
105171
105548
|
});
|
|
105172
105549
|
import { existsSync as existsSync26 } from "node:fs";
|
|
105173
|
-
import
|
|
105550
|
+
import path70 from "node:path";
|
|
105174
105551
|
function parse8(argv) {
|
|
105175
105552
|
const options = {
|
|
105176
105553
|
paths: [],
|
|
@@ -105275,8 +105652,8 @@ async function run23(argv, io) {
|
|
|
105275
105652
|
return emit(io, fail2("invalid_input", `${options}
|
|
105276
105653
|
usage: ${USAGE15}`));
|
|
105277
105654
|
const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
|
|
105278
|
-
const repo = top.ok ?
|
|
105279
|
-
const display = (file) => toPosix(
|
|
105655
|
+
const repo = top.ok ? path70.resolve(top.stdout.trim()) : null;
|
|
105656
|
+
const display = (file) => toPosix(path70.relative(io.cwd, file) || file);
|
|
105280
105657
|
let base = null;
|
|
105281
105658
|
let changed = null;
|
|
105282
105659
|
if (options.diff || options.mutate) {
|
|
@@ -105414,8 +105791,8 @@ var exports_youtrack = {};
|
|
|
105414
105791
|
__export(exports_youtrack, {
|
|
105415
105792
|
run: () => run25
|
|
105416
105793
|
});
|
|
105417
|
-
import { readFileSync as
|
|
105418
|
-
import
|
|
105794
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
105795
|
+
import path71 from "node:path";
|
|
105419
105796
|
async function run25(argv, io) {
|
|
105420
105797
|
const [sub, ...rest] = argv;
|
|
105421
105798
|
if (sub !== "note" && sub !== "time" && sub !== "meeting")
|
|
@@ -105461,7 +105838,7 @@ async function run25(argv, io) {
|
|
|
105461
105838
|
let markdown = flags.values.markdown ?? "";
|
|
105462
105839
|
if (flags.values.file)
|
|
105463
105840
|
try {
|
|
105464
|
-
markdown =
|
|
105841
|
+
markdown = readFileSync29(path71.resolve(io.cwd, flags.values.file), "utf8");
|
|
105465
105842
|
} catch (error) {
|
|
105466
105843
|
return usage4(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE17);
|
|
105467
105844
|
}
|
|
@@ -105529,8 +105906,8 @@ var exports_changelog = {};
|
|
|
105529
105906
|
__export(exports_changelog, {
|
|
105530
105907
|
run: () => run26
|
|
105531
105908
|
});
|
|
105532
|
-
import { readFileSync as
|
|
105533
|
-
import
|
|
105909
|
+
import { readFileSync as readFileSync30 } from "node:fs";
|
|
105910
|
+
import path72 from "node:path";
|
|
105534
105911
|
async function run26(argv, io) {
|
|
105535
105912
|
const [sub, ...rest] = argv;
|
|
105536
105913
|
if (sub !== "apply")
|
|
@@ -105576,7 +105953,7 @@ async function run26(argv, io) {
|
|
|
105576
105953
|
]);
|
|
105577
105954
|
}
|
|
105578
105955
|
var USAGE18 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
|
|
105579
|
-
const text = raw === "-" ?
|
|
105956
|
+
const text = raw === "-" ? readFileSync30(0, "utf8") : raw.startsWith("@") ? readFileSync30(path72.resolve(cwd, raw.slice(1)), "utf8") : raw;
|
|
105580
105957
|
return JSON.parse(text);
|
|
105581
105958
|
};
|
|
105582
105959
|
var init_changelog2 = __esm(() => {
|
|
@@ -105592,7 +105969,7 @@ __export(exports_handoff, {
|
|
|
105592
105969
|
run: () => run27
|
|
105593
105970
|
});
|
|
105594
105971
|
import fs37 from "node:fs";
|
|
105595
|
-
import
|
|
105972
|
+
import path73 from "node:path";
|
|
105596
105973
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
105597
105974
|
function render3(brief) {
|
|
105598
105975
|
const lines = [
|
|
@@ -105632,9 +106009,9 @@ function bounded(brief, cwd) {
|
|
|
105632
106009
|
let file = null;
|
|
105633
106010
|
if (store.ok)
|
|
105634
106011
|
try {
|
|
105635
|
-
const dir =
|
|
106012
|
+
const dir = path73.join(store.value.root, "handoff");
|
|
105636
106013
|
fs37.mkdirSync(dir, { recursive: true });
|
|
105637
|
-
file =
|
|
106014
|
+
file = path73.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
|
|
105638
106015
|
fs37.writeFileSync(file, `${[...body, ...tail].join(`
|
|
105639
106016
|
`)}
|
|
105640
106017
|
`, { mode: 384 });
|
|
@@ -105706,7 +106083,7 @@ var init_handoff = __esm(() => {
|
|
|
105706
106083
|
// packages/workit-cli/src/main.ts
|
|
105707
106084
|
init_host_session();
|
|
105708
106085
|
init_package();
|
|
105709
|
-
import
|
|
106086
|
+
import path74 from "node:path";
|
|
105710
106087
|
|
|
105711
106088
|
// packages/workit-cli/src/verbs/registry.ts
|
|
105712
106089
|
var TASK_FAMILY_NAMES = [
|
|
@@ -106034,7 +106411,7 @@ var VERBS = [
|
|
|
106034
106411
|
},
|
|
106035
106412
|
{
|
|
106036
106413
|
name: "merge",
|
|
106037
|
-
usage: "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>]",
|
|
106414
|
+
usage: "workit pr merge [--pr <n>] [--squash|--merge|--rebase | --method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch | --keep-branch] [--unverified --reason <why>] [--base-red-ok --reason <why>]",
|
|
106038
106415
|
summary: "Merge when READY, verified by a non-author and granted; --subject/--body set the squash message"
|
|
106039
106416
|
}
|
|
106040
106417
|
],
|
|
@@ -106612,7 +106989,7 @@ var defaultIo = (json) => ({
|
|
|
106612
106989
|
});
|
|
106613
106990
|
async function main(argv, overrides = {}, options = {}) {
|
|
106614
106991
|
const parsed = parseGlobals(argv);
|
|
106615
|
-
const cwd =
|
|
106992
|
+
const cwd = path74.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
|
|
106616
106993
|
const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
|
|
106617
106994
|
if (parsed.error)
|
|
106618
106995
|
return emit(io, fail2("invalid_input", parsed.error));
|