@brainervirus/workit-cursor 8.1.0 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +865 -125
- package/dist/mcp-server.js +161 -53
- package/dist/workit-hook.js +869 -99
- package/package.json +3 -3
- package/skills/workit-ship/SKILL.md +14 -14
package/dist/workit-hook.js
CHANGED
|
@@ -8527,9 +8527,28 @@ function patchId(cwd, base, head) {
|
|
|
8527
8527
|
const id = git(cwd, ["patch-id", "--stable"], { input: diff.stdout });
|
|
8528
8528
|
return line(id)?.split(/\s+/u)[0] ?? null;
|
|
8529
8529
|
}
|
|
8530
|
+
function remoteNames(cwd) {
|
|
8531
|
+
return (git(cwd, ["remote"]).stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
|
|
8532
|
+
}
|
|
8533
|
+
var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
8530
8534
|
function currentBranch(cwd) {
|
|
8531
8535
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
8532
8536
|
}
|
|
8537
|
+
function pushRemoteName(cwd, branch) {
|
|
8538
|
+
const name = branch ?? currentBranch(cwd);
|
|
8539
|
+
const remotes = remoteNames(cwd);
|
|
8540
|
+
const candidates = [
|
|
8541
|
+
name ? config2(cwd, `branch.${name}.pushRemote`) : null,
|
|
8542
|
+
config2(cwd, "remote.pushDefault"),
|
|
8543
|
+
name ? config2(cwd, `branch.${name}.remote`) : null
|
|
8544
|
+
];
|
|
8545
|
+
for (const candidate of candidates)
|
|
8546
|
+
if (candidate && candidate !== "." && remotes.includes(candidate))
|
|
8547
|
+
return candidate;
|
|
8548
|
+
if (remotes.includes("origin"))
|
|
8549
|
+
return "origin";
|
|
8550
|
+
return remotes.length === 1 ? remotes[0] : null;
|
|
8551
|
+
}
|
|
8533
8552
|
|
|
8534
8553
|
// packages/workit-core/src/ledger.ts
|
|
8535
8554
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -8537,6 +8556,35 @@ import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
|
8537
8556
|
import fs3 from "node:fs";
|
|
8538
8557
|
import path3 from "node:path";
|
|
8539
8558
|
|
|
8559
|
+
// packages/workit-core/src/host-session.ts
|
|
8560
|
+
var HOST_SESSION_VARS = [
|
|
8561
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
8562
|
+
["PI_SESSION_ID", () => "pi"],
|
|
8563
|
+
[
|
|
8564
|
+
"CODEX_THREAD_ID",
|
|
8565
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
8566
|
+
]
|
|
8567
|
+
];
|
|
8568
|
+
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
8569
|
+
function hostSessionFromEnv(env) {
|
|
8570
|
+
const value = (key) => env[key]?.trim() || null;
|
|
8571
|
+
let inner = null;
|
|
8572
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
8573
|
+
const session = value(key);
|
|
8574
|
+
if (session) {
|
|
8575
|
+
inner = { host: host(env), session };
|
|
8576
|
+
break;
|
|
8577
|
+
}
|
|
8578
|
+
}
|
|
8579
|
+
const declared = value("WORKIT_HOST");
|
|
8580
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
8581
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
8582
|
+
if (!inherited)
|
|
8583
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
8584
|
+
}
|
|
8585
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
8586
|
+
}
|
|
8587
|
+
|
|
8540
8588
|
// packages/workit-core/src/store/paths.ts
|
|
8541
8589
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
8542
8590
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -8819,6 +8867,7 @@ var checkoutSlug = (root) => {
|
|
|
8819
8867
|
|
|
8820
8868
|
// packages/workit-core/src/ledger.ts
|
|
8821
8869
|
var LEDGER_VERSION = 1;
|
|
8870
|
+
var MAX_LINE_BYTES = 4096;
|
|
8822
8871
|
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
8823
8872
|
var FAILING = new Set(["failed", "blocked"]);
|
|
8824
8873
|
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
@@ -8918,6 +8967,7 @@ function ledgerPath(cwd) {
|
|
|
8918
8967
|
const root = storeRoot(cwd);
|
|
8919
8968
|
return root.ok ? { ok: true, value: path3.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
8920
8969
|
}
|
|
8970
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
|
|
8921
8971
|
var NETWORK_FS_TYPES = new Set([
|
|
8922
8972
|
26985,
|
|
8923
8973
|
20859,
|
|
@@ -8930,6 +8980,158 @@ var NETWORK_FS_TYPES = new Set([
|
|
|
8930
8980
|
1196443219,
|
|
8931
8981
|
198183888
|
|
8932
8982
|
]);
|
|
8983
|
+
var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
|
|
8984
|
+
var onNetworkFs = (dir) => {
|
|
8985
|
+
if (process.platform !== "linux")
|
|
8986
|
+
return false;
|
|
8987
|
+
try {
|
|
8988
|
+
return isNetworkFsType(fs3.statfsSync(dir).type);
|
|
8989
|
+
} catch {
|
|
8990
|
+
return false;
|
|
8991
|
+
}
|
|
8992
|
+
};
|
|
8993
|
+
var LOCK_WAIT_MS = 5000;
|
|
8994
|
+
var LOCK_STALE_MS = 30000;
|
|
8995
|
+
var LOCK_OWNER = "owner";
|
|
8996
|
+
var lockToken = (lock) => {
|
|
8997
|
+
try {
|
|
8998
|
+
return fs3.readFileSync(path3.join(lock, LOCK_OWNER), "utf8");
|
|
8999
|
+
} catch {
|
|
9000
|
+
return null;
|
|
9001
|
+
}
|
|
9002
|
+
};
|
|
9003
|
+
var lockAgeMs = (lock) => {
|
|
9004
|
+
for (const target of [path3.join(lock, LOCK_OWNER), lock]) {
|
|
9005
|
+
try {
|
|
9006
|
+
return Date.now() - fs3.statSync(target).mtimeMs;
|
|
9007
|
+
} catch {}
|
|
9008
|
+
}
|
|
9009
|
+
return null;
|
|
9010
|
+
};
|
|
9011
|
+
var ledgerLock = {
|
|
9012
|
+
acquire(lock) {
|
|
9013
|
+
try {
|
|
9014
|
+
fs3.mkdirSync(lock);
|
|
9015
|
+
} catch (error) {
|
|
9016
|
+
if (error.code === "EEXIST")
|
|
9017
|
+
return null;
|
|
9018
|
+
throw error;
|
|
9019
|
+
}
|
|
9020
|
+
const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
|
|
9021
|
+
fs3.writeFileSync(path3.join(lock, LOCK_OWNER), token);
|
|
9022
|
+
return token;
|
|
9023
|
+
},
|
|
9024
|
+
release(lock, token) {
|
|
9025
|
+
if (lockToken(lock) !== token)
|
|
9026
|
+
return false;
|
|
9027
|
+
const tomb = `${lock}.release-${randomBytes(6).toString("hex")}`;
|
|
9028
|
+
try {
|
|
9029
|
+
fs3.renameSync(lock, tomb);
|
|
9030
|
+
} catch {
|
|
9031
|
+
return false;
|
|
9032
|
+
}
|
|
9033
|
+
if (lockToken(tomb) !== token) {
|
|
9034
|
+
try {
|
|
9035
|
+
fs3.renameSync(tomb, lock);
|
|
9036
|
+
} catch {}
|
|
9037
|
+
return false;
|
|
9038
|
+
}
|
|
9039
|
+
fs3.rmSync(tomb, { recursive: true, force: true });
|
|
9040
|
+
return true;
|
|
9041
|
+
},
|
|
9042
|
+
token: lockToken,
|
|
9043
|
+
reap(lock, observed) {
|
|
9044
|
+
const tomb = `${lock}.reap-${randomBytes(6).toString("hex")}`;
|
|
9045
|
+
try {
|
|
9046
|
+
fs3.renameSync(lock, tomb);
|
|
9047
|
+
} catch {
|
|
9048
|
+
return false;
|
|
9049
|
+
}
|
|
9050
|
+
if (lockToken(tomb) === observed) {
|
|
9051
|
+
fs3.rmSync(tomb, { recursive: true, force: true });
|
|
9052
|
+
return true;
|
|
9053
|
+
}
|
|
9054
|
+
try {
|
|
9055
|
+
fs3.renameSync(tomb, lock);
|
|
9056
|
+
} catch {
|
|
9057
|
+
fs3.rmSync(tomb, { recursive: true, force: true });
|
|
9058
|
+
}
|
|
9059
|
+
return false;
|
|
9060
|
+
}
|
|
9061
|
+
};
|
|
9062
|
+
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
9063
|
+
const lock = `${file}.lock`;
|
|
9064
|
+
const deadline = Date.now() + waitMs;
|
|
9065
|
+
let token = null;
|
|
9066
|
+
for (;; ) {
|
|
9067
|
+
token = ledgerLock.acquire(lock);
|
|
9068
|
+
if (token)
|
|
9069
|
+
break;
|
|
9070
|
+
const observed = lockToken(lock);
|
|
9071
|
+
const age = lockAgeMs(lock);
|
|
9072
|
+
if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
|
|
9073
|
+
continue;
|
|
9074
|
+
if (Date.now() >= deadline)
|
|
9075
|
+
return null;
|
|
9076
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
9077
|
+
}
|
|
9078
|
+
try {
|
|
9079
|
+
return fn();
|
|
9080
|
+
} finally {
|
|
9081
|
+
ledgerLock.release(lock, token);
|
|
9082
|
+
}
|
|
9083
|
+
}
|
|
9084
|
+
function appendLine(file, line, options) {
|
|
9085
|
+
const write = () => {
|
|
9086
|
+
const fd = fs3.openSync(file, "a+");
|
|
9087
|
+
try {
|
|
9088
|
+
const size = fs3.fstatSync(fd).size;
|
|
9089
|
+
let fence = "";
|
|
9090
|
+
if (size > 0) {
|
|
9091
|
+
const last = Buffer.alloc(1);
|
|
9092
|
+
fs3.readSync(fd, last, 0, 1, size - 1);
|
|
9093
|
+
if (last[0] !== 10)
|
|
9094
|
+
fence = `
|
|
9095
|
+
`;
|
|
9096
|
+
}
|
|
9097
|
+
const bytes = Buffer.from(fence + line);
|
|
9098
|
+
const written = fs3.writeSync(fd, bytes);
|
|
9099
|
+
if (written !== bytes.length)
|
|
9100
|
+
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
9101
|
+
return { ok: true, value: undefined };
|
|
9102
|
+
} finally {
|
|
9103
|
+
fs3.closeSync(fd);
|
|
9104
|
+
}
|
|
9105
|
+
};
|
|
9106
|
+
try {
|
|
9107
|
+
fs3.mkdirSync(path3.dirname(file), { recursive: true });
|
|
9108
|
+
if (!options.forceLock && !onNetworkFs(path3.dirname(file)))
|
|
9109
|
+
return write();
|
|
9110
|
+
const locked = withLedgerLock(file, write);
|
|
9111
|
+
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
9112
|
+
} catch (error) {
|
|
9113
|
+
return err("unavailable", `cannot append to ${file}: ${error.message}`);
|
|
9114
|
+
}
|
|
9115
|
+
}
|
|
9116
|
+
var serialize = (row, now) => {
|
|
9117
|
+
const full = { v: LEDGER_VERSION, id: newId2(), at: now.toISOString(), ...row };
|
|
9118
|
+
return { full, line: `${JSON.stringify(full)}
|
|
9119
|
+
` };
|
|
9120
|
+
};
|
|
9121
|
+
function appendRaw(cwd, row, options) {
|
|
9122
|
+
const { full, line } = serialize(row, options.now ?? new Date);
|
|
9123
|
+
const bytes = Buffer.byteLength(line);
|
|
9124
|
+
if (bytes > MAX_LINE_BYTES)
|
|
9125
|
+
return err("invalid_input", `ledger row is ${bytes} bytes; the limit is ${MAX_LINE_BYTES}. Shorten the text and pass details by --ref <path|url>`);
|
|
9126
|
+
const file = ledgerPath(cwd);
|
|
9127
|
+
if (!file.ok)
|
|
9128
|
+
return file;
|
|
9129
|
+
const written = appendLine(file.value, line, options);
|
|
9130
|
+
return written.ok ? { ok: true, value: full } : written;
|
|
9131
|
+
}
|
|
9132
|
+
function appendHookObserved(cwd, row) {
|
|
9133
|
+
return appendRaw(cwd, { ...row, observer: "host_hook" }, {});
|
|
9134
|
+
}
|
|
8933
9135
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8934
9136
|
var str = (value) => typeof value === "string" ? value : null;
|
|
8935
9137
|
var normalizeActor = (value) => {
|
|
@@ -10099,6 +10301,7 @@ var resolveCommitPolicy = (config, workspace) => {
|
|
|
10099
10301
|
};
|
|
10100
10302
|
|
|
10101
10303
|
// packages/workit-core/src/core/workspaces.ts
|
|
10304
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
10102
10305
|
import { createHash as createHash5 } from "node:crypto";
|
|
10103
10306
|
import { readFileSync as readFileSync2, realpathSync } from "node:fs";
|
|
10104
10307
|
import path6 from "node:path";
|
|
@@ -10410,6 +10613,53 @@ var validateWorkspaceGlob = (glob) => {
|
|
|
10410
10613
|
}
|
|
10411
10614
|
return { ok: true };
|
|
10412
10615
|
};
|
|
10616
|
+
|
|
10617
|
+
class WorkspaceConfigError extends Error {
|
|
10618
|
+
name = "WorkspaceConfigError";
|
|
10619
|
+
}
|
|
10620
|
+
var mainCheckouts = new Map;
|
|
10621
|
+
var mainCheckoutOf = (cwd) => {
|
|
10622
|
+
const cached = mainCheckouts.get(cwd);
|
|
10623
|
+
if (cached !== undefined)
|
|
10624
|
+
return cached;
|
|
10625
|
+
const env = { ...process.env };
|
|
10626
|
+
for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
|
|
10627
|
+
delete env[name];
|
|
10628
|
+
const git = (args) => {
|
|
10629
|
+
const run = spawnSync5("git", args, {
|
|
10630
|
+
cwd,
|
|
10631
|
+
env,
|
|
10632
|
+
encoding: "utf8",
|
|
10633
|
+
timeout: 5000,
|
|
10634
|
+
windowsHide: true
|
|
10635
|
+
});
|
|
10636
|
+
return run.status === 0 ? run.stdout : null;
|
|
10637
|
+
};
|
|
10638
|
+
let main = null;
|
|
10639
|
+
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
10640
|
+
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
10641
|
+
if (gitDir && common && path6.resolve(gitDir) !== path6.resolve(common)) {
|
|
10642
|
+
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
10643
|
+
main = listed?.trim() || (path6.basename(common) === ".git" ? path6.dirname(common) : common);
|
|
10644
|
+
}
|
|
10645
|
+
mainCheckouts.set(cwd, main);
|
|
10646
|
+
return main;
|
|
10647
|
+
};
|
|
10648
|
+
var matchingEntries = (cwd, entries) => {
|
|
10649
|
+
const match = (where) => {
|
|
10650
|
+
const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
|
|
10651
|
+
return entries.filter((entry) => {
|
|
10652
|
+
const glob = entry.glob.replaceAll("\\", "/");
|
|
10653
|
+
const canonical = canonicalGlob(glob);
|
|
10654
|
+
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
10655
|
+
});
|
|
10656
|
+
};
|
|
10657
|
+
const direct = match(cwd);
|
|
10658
|
+
if (direct.length)
|
|
10659
|
+
return direct;
|
|
10660
|
+
const main = mainCheckoutOf(cwd);
|
|
10661
|
+
return main ? match(main) : direct;
|
|
10662
|
+
};
|
|
10413
10663
|
var realpathOf = (p) => {
|
|
10414
10664
|
const real = realpathSync.native ?? realpathSync;
|
|
10415
10665
|
try {
|
|
@@ -10434,43 +10684,29 @@ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
|
|
|
10434
10684
|
if (named.length === 1)
|
|
10435
10685
|
return named[0];
|
|
10436
10686
|
if (named.length > 1)
|
|
10437
|
-
throw new
|
|
10687
|
+
throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
|
|
10438
10688
|
if (matches.length > 0) {
|
|
10439
|
-
throw new
|
|
10689
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
10440
10690
|
}
|
|
10441
|
-
throw new
|
|
10691
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
10442
10692
|
}
|
|
10443
10693
|
if (matches.length < 2)
|
|
10444
10694
|
return matches[0];
|
|
10445
10695
|
const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
|
|
10446
10696
|
const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
|
|
10447
10697
|
if (mostSpecific.length > 1) {
|
|
10448
|
-
throw new
|
|
10698
|
+
throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
|
|
10449
10699
|
}
|
|
10450
10700
|
return mostSpecific[0];
|
|
10451
10701
|
};
|
|
10452
10702
|
var resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
|
|
10453
|
-
const
|
|
10454
|
-
const matches = [];
|
|
10455
|
-
for (const ws of entries) {
|
|
10456
|
-
const glob = ws.glob.replaceAll("\\", "/");
|
|
10457
|
-
const canonical = canonicalGlob(glob);
|
|
10458
|
-
let matched = false;
|
|
10459
|
-
for (const target of targets) {
|
|
10460
|
-
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
10461
|
-
matched = true;
|
|
10462
|
-
break;
|
|
10463
|
-
}
|
|
10464
|
-
}
|
|
10465
|
-
if (matched)
|
|
10466
|
-
matches.push(ws);
|
|
10467
|
-
}
|
|
10703
|
+
const matches = matchingEntries(cwd, entries);
|
|
10468
10704
|
return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
|
|
10469
10705
|
};
|
|
10470
10706
|
var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
10471
10707
|
const result = readWorkspacesResult(dir);
|
|
10472
10708
|
if (result.status === "malformed" || result.status === "invalid")
|
|
10473
|
-
throw new
|
|
10709
|
+
throw new WorkspaceConfigError(result.error);
|
|
10474
10710
|
return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
|
|
10475
10711
|
};
|
|
10476
10712
|
var runtimeWorkspacePolicySchema = (kind) => {
|
|
@@ -10490,6 +10726,7 @@ var runtimeWorkspacePolicySchema = (kind) => {
|
|
|
10490
10726
|
}).passthrough();
|
|
10491
10727
|
return entrySchema;
|
|
10492
10728
|
};
|
|
10729
|
+
var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs");
|
|
10493
10730
|
var runtimeWorkspaceIndexSchema = object({
|
|
10494
10731
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
10495
10732
|
}).passthrough();
|
|
@@ -10501,31 +10738,26 @@ var matchRuntimeWorkspace = (cwd) => {
|
|
|
10501
10738
|
} catch (error) {
|
|
10502
10739
|
if (error.code === "ENOENT")
|
|
10503
10740
|
return null;
|
|
10504
|
-
throw new
|
|
10741
|
+
throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
10505
10742
|
}
|
|
10506
10743
|
let parsed;
|
|
10507
10744
|
try {
|
|
10508
10745
|
parsed = JSON.parse(raw);
|
|
10509
10746
|
} catch {
|
|
10510
|
-
throw new
|
|
10747
|
+
throw new WorkspaceConfigError(`${file} is not valid JSON`);
|
|
10511
10748
|
}
|
|
10512
10749
|
const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
|
|
10513
10750
|
if (!validated.success) {
|
|
10514
10751
|
const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
|
|
10515
|
-
throw new
|
|
10752
|
+
throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
|
|
10516
10753
|
}
|
|
10517
10754
|
const entries = validated.data.workspaces;
|
|
10518
10755
|
for (const entry of entries ?? []) {
|
|
10519
10756
|
const glob = validateWorkspaceGlob(entry.glob);
|
|
10520
10757
|
if (!glob.ok)
|
|
10521
|
-
throw new
|
|
10758
|
+
throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
10522
10759
|
}
|
|
10523
|
-
const
|
|
10524
|
-
const matches = (entries ?? []).filter((entry) => {
|
|
10525
|
-
const glob = entry.glob.replaceAll("\\", "/");
|
|
10526
|
-
const canonical = canonicalGlob(glob);
|
|
10527
|
-
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
10528
|
-
});
|
|
10760
|
+
const matches = matchingEntries(cwd, entries ?? []);
|
|
10529
10761
|
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
10530
10762
|
const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
|
|
10531
10763
|
return selected ? { file, selected } : null;
|
|
@@ -10542,7 +10774,7 @@ var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
|
10542
10774
|
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
10543
10775
|
if (!policy.success) {
|
|
10544
10776
|
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
10545
|
-
throw new
|
|
10777
|
+
throw new WorkspaceConfigError(`${file} has invalid ${kind} policy configuration: ${detail}`);
|
|
10546
10778
|
}
|
|
10547
10779
|
return policy.data;
|
|
10548
10780
|
};
|
|
@@ -10553,10 +10785,10 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
|
10553
10785
|
const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
|
|
10554
10786
|
const profileName = requestedProfile?.trim() || candidate.defaultProfile;
|
|
10555
10787
|
if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
|
|
10556
|
-
throw new
|
|
10788
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
|
|
10557
10789
|
const profile = profileName ? candidate.profiles?.[profileName] : undefined;
|
|
10558
10790
|
if (profileName && !profile)
|
|
10559
|
-
throw new
|
|
10791
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
|
|
10560
10792
|
const profilePolicy = profile?.[key];
|
|
10561
10793
|
const workspacePolicy = candidate[key];
|
|
10562
10794
|
return {
|
|
@@ -11321,7 +11553,7 @@ var packageRoot = () => {
|
|
|
11321
11553
|
};
|
|
11322
11554
|
|
|
11323
11555
|
// packages/workit-core/src/core/store-lock.ts
|
|
11324
|
-
import { spawnSync as
|
|
11556
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
11325
11557
|
import fs7 from "node:fs";
|
|
11326
11558
|
import { hostname } from "node:os";
|
|
11327
11559
|
var metadataLockSchema = object({
|
|
@@ -11371,7 +11603,7 @@ var processStartOf = (pid) => {
|
|
|
11371
11603
|
}
|
|
11372
11604
|
if (process.platform === "darwin" || process.platform === "freebsd") {
|
|
11373
11605
|
try {
|
|
11374
|
-
const run =
|
|
11606
|
+
const run = spawnSync6("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
11375
11607
|
encoding: "utf8",
|
|
11376
11608
|
timeout: 1000
|
|
11377
11609
|
});
|
|
@@ -11983,9 +12215,10 @@ var drop = (lock) => {
|
|
|
11983
12215
|
};
|
|
11984
12216
|
var envActor = () => {
|
|
11985
12217
|
const value = (key) => process.env[key]?.trim() || null;
|
|
12218
|
+
const acting = hostSessionFromEnv(process.env);
|
|
11986
12219
|
return {
|
|
11987
|
-
host:
|
|
11988
|
-
session:
|
|
12220
|
+
host: acting.host ?? "runtime",
|
|
12221
|
+
session: acting.session,
|
|
11989
12222
|
agentId: value("WORKIT_AGENT_ID")
|
|
11990
12223
|
};
|
|
11991
12224
|
};
|
|
@@ -13511,11 +13744,11 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
|
|
|
13511
13744
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
13512
13745
|
import { createHash as createHash8 } from "node:crypto";
|
|
13513
13746
|
import * as fs11 from "node:fs";
|
|
13514
|
-
import { spawnSync as
|
|
13747
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
13515
13748
|
import path14 from "node:path";
|
|
13516
13749
|
|
|
13517
13750
|
// packages/workit-core/src/check-config.ts
|
|
13518
|
-
import { spawnSync as
|
|
13751
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
13519
13752
|
import fs10 from "node:fs";
|
|
13520
13753
|
import path13 from "node:path";
|
|
13521
13754
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
@@ -13559,7 +13792,7 @@ function splitCommand(command) {
|
|
|
13559
13792
|
return argv.length ? argv : new Error("empty command");
|
|
13560
13793
|
}
|
|
13561
13794
|
var git3 = (cwd, args) => {
|
|
13562
|
-
const run =
|
|
13795
|
+
const run = spawnSync7("git", args, {
|
|
13563
13796
|
cwd,
|
|
13564
13797
|
encoding: "utf8",
|
|
13565
13798
|
timeout: 20000,
|
|
@@ -13764,7 +13997,7 @@ var compareCodeUnits3 = (left, right) => {
|
|
|
13764
13997
|
return left.length - right.length;
|
|
13765
13998
|
};
|
|
13766
13999
|
var gitPaths = (root) => {
|
|
13767
|
-
const result =
|
|
14000
|
+
const result = spawnSync8("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
13768
14001
|
cwd: root,
|
|
13769
14002
|
encoding: "buffer"
|
|
13770
14003
|
});
|
|
@@ -13777,7 +14010,7 @@ var gitPaths = (root) => {
|
|
|
13777
14010
|
};
|
|
13778
14011
|
}
|
|
13779
14012
|
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")) : [];
|
|
13780
|
-
const stagedDeleted =
|
|
14013
|
+
const stagedDeleted = spawnSync8("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
13781
14014
|
if (stagedDeleted.status !== 0) {
|
|
13782
14015
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
13783
14016
|
if (!/not a git repository/i.test(stderr))
|
|
@@ -13908,7 +14141,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
13908
14141
|
const values = supplied.toSorted((left, right) => compareCodeUnits3(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
13909
14142
|
if (values.some(({ value }) => value === null))
|
|
13910
14143
|
completeness = "uncertain";
|
|
13911
|
-
const headResult =
|
|
14144
|
+
const headResult = spawnSync8("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
13912
14145
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
13913
14146
|
completeness = "uncertain";
|
|
13914
14147
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -16599,6 +16832,7 @@ var validateBranchNameFor = (workspaceRoot, name) => {
|
|
|
16599
16832
|
};
|
|
16600
16833
|
}
|
|
16601
16834
|
};
|
|
16835
|
+
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
16602
16836
|
|
|
16603
16837
|
// packages/workit-core/src/core/route-intent.ts
|
|
16604
16838
|
var shellBranchTarget = (command) => {
|
|
@@ -16624,36 +16858,43 @@ var shellPolicy = (cwd, command) => {
|
|
|
16624
16858
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
16625
16859
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
16626
16860
|
import path16 from "node:path";
|
|
16627
|
-
|
|
16628
|
-
|
|
16629
|
-
var
|
|
16630
|
-
|
|
16631
|
-
for (let
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16861
|
+
|
|
16862
|
+
// packages/workit-core/src/hooks/shell-words.ts
|
|
16863
|
+
var closingQuote = (command, start, dialect) => {
|
|
16864
|
+
const quote = command[start];
|
|
16865
|
+
for (let index = start + 1;index < command.length; index++) {
|
|
16866
|
+
const char = command[index];
|
|
16867
|
+
if (quote === "'") {
|
|
16868
|
+
if (char !== "'")
|
|
16869
|
+
continue;
|
|
16870
|
+
if (dialect === "powershell" && command[index + 1] === "'") {
|
|
16871
|
+
index++;
|
|
16872
|
+
continue;
|
|
16873
|
+
}
|
|
16874
|
+
return index;
|
|
16875
|
+
}
|
|
16876
|
+
if (dialect === "posix" && char === "\\" || dialect === "powershell" && char === "`") {
|
|
16877
|
+
index++;
|
|
16878
|
+
continue;
|
|
16638
16879
|
}
|
|
16880
|
+
if (char === '"')
|
|
16881
|
+
return index;
|
|
16639
16882
|
}
|
|
16883
|
+
return -1;
|
|
16640
16884
|
};
|
|
16641
|
-
var
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
return true;
|
|
16646
|
-
const posix = relative.split(path16.sep).join("/");
|
|
16647
|
-
const segments = posix.split("/");
|
|
16648
|
-
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
16885
|
+
var unquote = (body, quote, dialect) => {
|
|
16886
|
+
if (quote === "'")
|
|
16887
|
+
return dialect === "powershell" ? body.replaceAll("''", "'") : body;
|
|
16888
|
+
return dialect === "posix" ? body.replace(/\\(["\\$`])/g, "$1") : body.replace(/`(.)/g, "$1");
|
|
16649
16889
|
};
|
|
16650
|
-
var segmentsOf = (command) => {
|
|
16890
|
+
var segmentsOf = (command, dialect = "posix") => {
|
|
16651
16891
|
const segments = [];
|
|
16652
16892
|
let current = { words: [], redirects: [] };
|
|
16653
16893
|
let word = "";
|
|
16654
16894
|
let inWord = false;
|
|
16655
16895
|
let pending = null;
|
|
16656
16896
|
const heredocs = [];
|
|
16897
|
+
const escape = dialect === "posix" ? "\\" : "`";
|
|
16657
16898
|
const flushWord = () => {
|
|
16658
16899
|
if (!inWord)
|
|
16659
16900
|
return;
|
|
@@ -16677,11 +16918,11 @@ var segmentsOf = (command) => {
|
|
|
16677
16918
|
for (let index = 0;index < command.length; index++) {
|
|
16678
16919
|
const char = command[index];
|
|
16679
16920
|
if (char === "'" || char === '"') {
|
|
16680
|
-
const end = command
|
|
16681
|
-
word += command.slice(index + 1, end < 0 ? undefined : end);
|
|
16921
|
+
const end = closingQuote(command, index, dialect);
|
|
16922
|
+
word += unquote(command.slice(index + 1, end < 0 ? undefined : end), char, dialect);
|
|
16682
16923
|
inWord = true;
|
|
16683
16924
|
index = end < 0 ? command.length : end;
|
|
16684
|
-
} else if (char ===
|
|
16925
|
+
} else if (char === escape && index + 1 < command.length) {
|
|
16685
16926
|
word += command[++index];
|
|
16686
16927
|
inWord = true;
|
|
16687
16928
|
} else if (char === `
|
|
@@ -16698,7 +16939,14 @@ var segmentsOf = (command) => {
|
|
|
16698
16939
|
}
|
|
16699
16940
|
} else if (/\s/.test(char))
|
|
16700
16941
|
flushWord();
|
|
16701
|
-
else if ("
|
|
16942
|
+
else if (char === "(" || char === ")") {
|
|
16943
|
+
if (char === "(" && inWord && word.endsWith("$")) {
|
|
16944
|
+
word = word.slice(0, -1);
|
|
16945
|
+
inWord = word !== "";
|
|
16946
|
+
}
|
|
16947
|
+
flushSegment();
|
|
16948
|
+
segments.push({ words: [], redirects: [], group: char === "(" ? "open" : "close" });
|
|
16949
|
+
} else if (";&|".includes(char)) {
|
|
16702
16950
|
if (char === "&" && command[index + 1] === ">")
|
|
16703
16951
|
continue;
|
|
16704
16952
|
flushSegment();
|
|
@@ -16716,7 +16964,7 @@ var segmentsOf = (command) => {
|
|
|
16716
16964
|
inWord = true;
|
|
16717
16965
|
}
|
|
16718
16966
|
pending = "redirect";
|
|
16719
|
-
} else if (char === "<") {
|
|
16967
|
+
} else if (char === "<" && dialect === "posix") {
|
|
16720
16968
|
flushWord();
|
|
16721
16969
|
if (command[index + 1] === "<") {
|
|
16722
16970
|
index++;
|
|
@@ -16733,6 +16981,31 @@ var segmentsOf = (command) => {
|
|
|
16733
16981
|
flushSegment();
|
|
16734
16982
|
return segments;
|
|
16735
16983
|
};
|
|
16984
|
+
|
|
16985
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
16986
|
+
var NONE2 = { kind: "none" };
|
|
16987
|
+
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
16988
|
+
var canonical2 = (file) => {
|
|
16989
|
+
let rest = "";
|
|
16990
|
+
for (let dir = path16.resolve(file);; dir = path16.dirname(dir)) {
|
|
16991
|
+
try {
|
|
16992
|
+
return path16.join(realpathSync5(dir), rest);
|
|
16993
|
+
} catch {
|
|
16994
|
+
if (path16.dirname(dir) === dir)
|
|
16995
|
+
return path16.resolve(file);
|
|
16996
|
+
rest = rest ? path16.join(path16.basename(dir), rest) : path16.basename(dir);
|
|
16997
|
+
}
|
|
16998
|
+
}
|
|
16999
|
+
};
|
|
17000
|
+
var exempt = (cwd, root, file, plans) => {
|
|
17001
|
+
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
17002
|
+
const relative = path16.relative(fold(canonical2(root)), fold(canonical2(path16.resolve(cwd, file))));
|
|
17003
|
+
if (relative.startsWith("..") || path16.isAbsolute(relative))
|
|
17004
|
+
return true;
|
|
17005
|
+
const posix = relative.split(path16.sep).join("/");
|
|
17006
|
+
const segments = posix.split("/");
|
|
17007
|
+
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
17008
|
+
};
|
|
16736
17009
|
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
16737
17010
|
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
16738
17011
|
var editsOf = (input) => {
|
|
@@ -16882,14 +17155,473 @@ function writeGate(cwd, paths) {
|
|
|
16882
17155
|
return NONE2;
|
|
16883
17156
|
}
|
|
16884
17157
|
}
|
|
17158
|
+
// packages/workit-core/src/hooks/raw-git.ts
|
|
17159
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
17160
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
17161
|
+
import fs12 from "node:fs";
|
|
17162
|
+
import os4 from "node:os";
|
|
17163
|
+
import path17 from "node:path";
|
|
17164
|
+
var NONE3 = { kind: "none" };
|
|
17165
|
+
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
17166
|
+
var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
17167
|
+
var SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
|
|
17168
|
+
var CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
17169
|
+
var PUSHD = new Set(["pushd", "push-location"]);
|
|
17170
|
+
var POPD = new Set(["popd", "pop-location"]);
|
|
17171
|
+
var GIT_VALUE_OPTIONS = new Set([
|
|
17172
|
+
"-c",
|
|
17173
|
+
"--git-dir",
|
|
17174
|
+
"--work-tree",
|
|
17175
|
+
"--namespace",
|
|
17176
|
+
"--exec-path",
|
|
17177
|
+
"--config-env",
|
|
17178
|
+
"--super-prefix"
|
|
17179
|
+
]);
|
|
17180
|
+
var isAbsolute = (value) => path17.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
17181
|
+
var joinDir = (base, next) => {
|
|
17182
|
+
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
17183
|
+
return;
|
|
17184
|
+
const expanded = next === "~" || next.startsWith("~/") ? path17.join(os4.homedir(), next.slice(1)) : next;
|
|
17185
|
+
return base === null || isAbsolute(expanded) ? expanded : path17.join(base, expanded);
|
|
17186
|
+
};
|
|
17187
|
+
var cdTarget = (words) => {
|
|
17188
|
+
const args = words.slice(1);
|
|
17189
|
+
const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
|
|
17190
|
+
if (flag >= 0)
|
|
17191
|
+
return args[flag + 1] ?? null;
|
|
17192
|
+
return args.find((word) => word === "-" || !word.startsWith("-")) ?? null;
|
|
17193
|
+
};
|
|
17194
|
+
var stripPrefixes = (input) => {
|
|
17195
|
+
let words = input;
|
|
17196
|
+
for (;; ) {
|
|
17197
|
+
const first = words[0];
|
|
17198
|
+
if (first === undefined)
|
|
17199
|
+
return words;
|
|
17200
|
+
if (/^\w+=/.test(first))
|
|
17201
|
+
words = words.slice(1);
|
|
17202
|
+
else if (PREFIXES3.has(first)) {
|
|
17203
|
+
words = words.slice(1);
|
|
17204
|
+
while (words[0]?.startsWith("-"))
|
|
17205
|
+
words = words.slice(words[0] === "-u" ? 2 : 1);
|
|
17206
|
+
} else if (first === "timeout") {
|
|
17207
|
+
words = words.slice(1);
|
|
17208
|
+
while (words[0]?.startsWith("-"))
|
|
17209
|
+
words = words.slice(1);
|
|
17210
|
+
words = words.slice(1);
|
|
17211
|
+
} else
|
|
17212
|
+
return words;
|
|
17213
|
+
}
|
|
17214
|
+
};
|
|
17215
|
+
var commandName = (word) => (word?.split(/[\\/]/).at(-1) ?? "").toLowerCase().replace(/\.exe$/, "");
|
|
17216
|
+
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
17217
|
+
const found = [];
|
|
17218
|
+
let dir = null;
|
|
17219
|
+
const groups = [];
|
|
17220
|
+
let stack = [];
|
|
17221
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
17222
|
+
if (segment.group === "open") {
|
|
17223
|
+
groups.push({ dir, stack: [...stack] });
|
|
17224
|
+
continue;
|
|
17225
|
+
}
|
|
17226
|
+
if (segment.group === "close") {
|
|
17227
|
+
const saved = groups.pop();
|
|
17228
|
+
if (saved)
|
|
17229
|
+
({ dir, stack } = saved);
|
|
17230
|
+
continue;
|
|
17231
|
+
}
|
|
17232
|
+
const words = stripPrefixes(segment.words);
|
|
17233
|
+
const name = commandName(words[0]);
|
|
17234
|
+
if (CD.has(name)) {
|
|
17235
|
+
const target = cdTarget(words);
|
|
17236
|
+
dir = target === null ? os4.homedir() : joinDir(dir, target);
|
|
17237
|
+
continue;
|
|
17238
|
+
}
|
|
17239
|
+
if (PUSHD.has(name)) {
|
|
17240
|
+
const target = cdTarget(words);
|
|
17241
|
+
stack.push(dir);
|
|
17242
|
+
dir = target === null || target === "-" ? undefined : joinDir(dir, target);
|
|
17243
|
+
continue;
|
|
17244
|
+
}
|
|
17245
|
+
if (POPD.has(name)) {
|
|
17246
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
17247
|
+
continue;
|
|
17248
|
+
}
|
|
17249
|
+
if (SHELLS.has(name) && depth < 3) {
|
|
17250
|
+
const flag = words.findIndex((word, index) => index > 0 && SCRIPT_FLAG.test(word));
|
|
17251
|
+
const script = flag > 0 ? words[flag + 1] : undefined;
|
|
17252
|
+
if (script)
|
|
17253
|
+
for (const inner of rawInvocations(script, "posix", depth + 1))
|
|
17254
|
+
found.push({
|
|
17255
|
+
...inner,
|
|
17256
|
+
dir: inner.dir === null ? dir : inner.dir === undefined ? undefined : joinDir(dir, inner.dir)
|
|
17257
|
+
});
|
|
17258
|
+
continue;
|
|
17259
|
+
}
|
|
17260
|
+
if (name === "gh" || name === "glab") {
|
|
17261
|
+
found.push({ tool: name, dir, args: words.slice(1) });
|
|
17262
|
+
continue;
|
|
17263
|
+
}
|
|
17264
|
+
if (name !== "git")
|
|
17265
|
+
continue;
|
|
17266
|
+
let here = dir;
|
|
17267
|
+
let index = 1;
|
|
17268
|
+
for (;index < words.length; index++) {
|
|
17269
|
+
const word = words[index];
|
|
17270
|
+
if (word === "-C")
|
|
17271
|
+
here = words[index + 1] ? joinDir(here, words[++index]) : here;
|
|
17272
|
+
else if (GIT_VALUE_OPTIONS.has(word))
|
|
17273
|
+
index++;
|
|
17274
|
+
else if (!word.startsWith("-"))
|
|
17275
|
+
break;
|
|
17276
|
+
}
|
|
17277
|
+
found.push({ tool: "git", dir: here, args: words.slice(index) });
|
|
17278
|
+
}
|
|
17279
|
+
return found;
|
|
17280
|
+
}
|
|
17281
|
+
var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
17282
|
+
var pushAction = (args) => {
|
|
17283
|
+
let force = false;
|
|
17284
|
+
let lease = false;
|
|
17285
|
+
let wholesale = false;
|
|
17286
|
+
let remote = null;
|
|
17287
|
+
const positionals = [];
|
|
17288
|
+
for (let index = 1;index < args.length; index++) {
|
|
17289
|
+
const arg = args[index];
|
|
17290
|
+
if (arg === "--") {
|
|
17291
|
+
positionals.push(...args.slice(index + 1));
|
|
17292
|
+
break;
|
|
17293
|
+
}
|
|
17294
|
+
if (arg === "--dry-run" || /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg))
|
|
17295
|
+
return null;
|
|
17296
|
+
if (arg === "--repo")
|
|
17297
|
+
remote = args[++index] ?? null;
|
|
17298
|
+
else if (arg.startsWith("--repo="))
|
|
17299
|
+
remote = arg.slice("--repo=".length);
|
|
17300
|
+
else if (PUSH_VALUE_OPTIONS.has(arg))
|
|
17301
|
+
index++;
|
|
17302
|
+
else if (arg === "--force" || /^-[a-zA-Z]*f[a-zA-Z]*$/.test(arg))
|
|
17303
|
+
force = true;
|
|
17304
|
+
else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
|
|
17305
|
+
lease = true;
|
|
17306
|
+
else if (["--all", "--mirror", "--branches", "--tags"].includes(arg))
|
|
17307
|
+
wholesale = true;
|
|
17308
|
+
else if (!arg.startsWith("-"))
|
|
17309
|
+
positionals.push(arg);
|
|
17310
|
+
}
|
|
17311
|
+
const specs = remote === null ? positionals.slice(1) : positionals;
|
|
17312
|
+
remote ??= positionals[0] ?? null;
|
|
17313
|
+
const targets = [];
|
|
17314
|
+
for (const raw of specs) {
|
|
17315
|
+
if (raw.startsWith("+"))
|
|
17316
|
+
force = true;
|
|
17317
|
+
const spec = raw.replace(/^\+/, "");
|
|
17318
|
+
const colon = spec.indexOf(":");
|
|
17319
|
+
const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
|
|
17320
|
+
if (!destination)
|
|
17321
|
+
continue;
|
|
17322
|
+
if (destination === "HEAD")
|
|
17323
|
+
targets.push(null);
|
|
17324
|
+
else if (destination.startsWith("refs/heads/"))
|
|
17325
|
+
targets.push(destination.slice(11));
|
|
17326
|
+
else if (!destination.startsWith("refs/"))
|
|
17327
|
+
targets.push(destination);
|
|
17328
|
+
}
|
|
17329
|
+
if (specs.length === 0 && !wholesale)
|
|
17330
|
+
targets.push(null);
|
|
17331
|
+
return { kind: "push", targets, blindForce: force && !lease, remote };
|
|
17332
|
+
};
|
|
17333
|
+
var VALUE_OPTIONS = {
|
|
17334
|
+
git: new Set([
|
|
17335
|
+
"-m",
|
|
17336
|
+
"--message",
|
|
17337
|
+
"-F",
|
|
17338
|
+
"--file",
|
|
17339
|
+
"-C",
|
|
17340
|
+
"-c",
|
|
17341
|
+
"--reuse-message",
|
|
17342
|
+
"--reedit-message",
|
|
17343
|
+
"--author",
|
|
17344
|
+
"--date",
|
|
17345
|
+
"--trailer",
|
|
17346
|
+
"-t",
|
|
17347
|
+
"--template",
|
|
17348
|
+
"--cleanup",
|
|
17349
|
+
"--fixup",
|
|
17350
|
+
"--squash",
|
|
17351
|
+
"-o",
|
|
17352
|
+
"--push-option",
|
|
17353
|
+
"--repo",
|
|
17354
|
+
"--receive-pack",
|
|
17355
|
+
"--exec"
|
|
17356
|
+
]),
|
|
17357
|
+
gh: new Set(["-b", "--body", "-F", "--body-file", "-t", "--title", "--subject", "-A", "--author-email"].concat(["-B", "--base", "-H", "--head", "-l", "--label", "-a", "--assignee"]).concat(["-r", "--reviewer", "-m", "--milestone", "-p", "--project", "-T", "--template"]).concat(["--match-head-commit", "-R", "--repo"])),
|
|
17358
|
+
glab: new Set(["-m", "--message", "--squash-message", "--sha", "-t", "--title", "-d", "--description"].concat(["-b", "--target-branch", "-s", "--source-branch", "-l", "--label"]).concat(["-a", "--assignee", "--reviewer", "-R", "--repo"]))
|
|
17359
|
+
};
|
|
17360
|
+
var asksHelp = (invocation) => {
|
|
17361
|
+
const { tool, args } = invocation;
|
|
17362
|
+
if (args[0] === "help" || tool !== "git" && args[1] === "help")
|
|
17363
|
+
return true;
|
|
17364
|
+
const values = VALUE_OPTIONS[tool];
|
|
17365
|
+
for (let index = 0;index < args.length; index++) {
|
|
17366
|
+
const arg = args[index];
|
|
17367
|
+
if (arg === "--")
|
|
17368
|
+
return false;
|
|
17369
|
+
if (arg === "--help" || arg === "-h")
|
|
17370
|
+
return true;
|
|
17371
|
+
if (values.has(arg))
|
|
17372
|
+
index++;
|
|
17373
|
+
}
|
|
17374
|
+
return false;
|
|
17375
|
+
};
|
|
17376
|
+
function rawAction(invocation) {
|
|
17377
|
+
const args = invocation.args;
|
|
17378
|
+
if (asksHelp(invocation))
|
|
17379
|
+
return null;
|
|
17380
|
+
const [first, second] = args;
|
|
17381
|
+
if (invocation.tool === "git") {
|
|
17382
|
+
if (first === "commit")
|
|
17383
|
+
return args.includes("--dry-run") ? null : { kind: "commit", amend: args.includes("--amend") };
|
|
17384
|
+
if (first === "push")
|
|
17385
|
+
return pushAction(args);
|
|
17386
|
+
return null;
|
|
17387
|
+
}
|
|
17388
|
+
const forge = invocation.tool;
|
|
17389
|
+
const noun = forge === "gh" ? "pr" : "mr";
|
|
17390
|
+
if (first !== noun)
|
|
17391
|
+
return null;
|
|
17392
|
+
if (second === "merge")
|
|
17393
|
+
return { kind: "merge", forge };
|
|
17394
|
+
if (second === "create")
|
|
17395
|
+
return { kind: "pr-create", forge };
|
|
17396
|
+
if (["view", "checks", "status", "ci", "list"].includes(second ?? ""))
|
|
17397
|
+
return { kind: "pr-read", forge };
|
|
17398
|
+
return null;
|
|
17399
|
+
}
|
|
17400
|
+
var repoAt = (dir) => {
|
|
17401
|
+
const location = resolveStore(dir);
|
|
17402
|
+
if (location instanceof Error || !location.shared)
|
|
17403
|
+
return null;
|
|
17404
|
+
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
17405
|
+
};
|
|
17406
|
+
var isWorkitWorkspace = (repo) => {
|
|
17407
|
+
if (fs12.existsSync(repo.location.dir))
|
|
17408
|
+
return true;
|
|
17409
|
+
try {
|
|
17410
|
+
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
17411
|
+
} catch {
|
|
17412
|
+
return false;
|
|
17413
|
+
}
|
|
17414
|
+
};
|
|
17415
|
+
var guardedBranch = (dir, branch) => {
|
|
17416
|
+
try {
|
|
17417
|
+
const policy = resolveBranchPolicyFor(dir);
|
|
17418
|
+
return policy.protected.has(branch.toLowerCase()) || policy.defaultTargetBranch.toLowerCase() === branch.toLowerCase();
|
|
17419
|
+
} catch {
|
|
17420
|
+
return false;
|
|
17421
|
+
}
|
|
17422
|
+
};
|
|
17423
|
+
var toPushRemote = (repo, remote) => {
|
|
17424
|
+
if (remote === null)
|
|
17425
|
+
return true;
|
|
17426
|
+
try {
|
|
17427
|
+
const pushRemote = pushRemoteName(repo.dir, repo.branch);
|
|
17428
|
+
return pushRemote === null || pushRemote === remote;
|
|
17429
|
+
} catch {
|
|
17430
|
+
return false;
|
|
17431
|
+
}
|
|
17432
|
+
};
|
|
17433
|
+
var headSha2 = (repo) => {
|
|
17434
|
+
const git = repo.location.git;
|
|
17435
|
+
if (!git || git.reftable)
|
|
17436
|
+
return null;
|
|
17437
|
+
try {
|
|
17438
|
+
const head = fs12.readFileSync(path17.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
17439
|
+
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
17440
|
+
return head;
|
|
17441
|
+
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
17442
|
+
if (!ref)
|
|
17443
|
+
return null;
|
|
17444
|
+
const common = path17.dirname(repo.location.dir);
|
|
17445
|
+
try {
|
|
17446
|
+
return fs12.readFileSync(path17.join(common, ref), "utf8").trim() || null;
|
|
17447
|
+
} catch {}
|
|
17448
|
+
const packed = fs12.readFileSync(path17.join(common, "packed-refs"), "utf8");
|
|
17449
|
+
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
17450
|
+
} catch {
|
|
17451
|
+
return null;
|
|
17452
|
+
}
|
|
17453
|
+
};
|
|
17454
|
+
var quote = (text) => `\`${text}\``;
|
|
17455
|
+
var refusal = (action, branch) => {
|
|
17456
|
+
if (action.kind === "merge")
|
|
17457
|
+
return `workit: raw ${quote(action.forge === "gh" ? "gh pr merge" : "glab mr merge")} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
|
|
17458
|
+
return `workit: raw ${quote("git push")}${action.kind === "push" && action.blindForce ? " --force" : ""} onto ${branch}, a protected branch or the default target, bypasses workit's protected-branch check. Push your feature branch with ${quote("workit git push")} (${quote("workit git push --force-with-lease")} to rewrite it) and land it with ${quote("workit pr merge")}.`;
|
|
17459
|
+
};
|
|
17460
|
+
var nudgeFor = (action, host, session) => {
|
|
17461
|
+
switch (action.kind) {
|
|
17462
|
+
case "commit": {
|
|
17463
|
+
const prefix = host === "cursor" && session ? `WORKIT_SESSION_ID=${session} ` : "";
|
|
17464
|
+
return action.amend ? `workit: raw ${quote("git commit --amend")} is recorded for this session; for new commits prefer ${quote(`${prefix}workit git commit -m '<msg>'`)} (adds the Workit-Session trailer and checks branch and commit policy).` : `workit: prefer ${quote(`${prefix}workit git commit -m '<msg>' [--all | -- <paths>]`)}: it adds the Workit-Session trailer and checks branch and commit policy.`;
|
|
17465
|
+
}
|
|
17466
|
+
case "push":
|
|
17467
|
+
return action.blindForce ? `workit: ${quote("git push --force")} overwrites without a lease; use ${quote("workit git push --force-with-lease")}.` : `workit: prefer ${quote("workit git push")}: it refuses protected branches and verifies the remote tip.`;
|
|
17468
|
+
case "pr-create":
|
|
17469
|
+
return `workit: prefer ${quote("workit pr create --fill")} (or --title/--body): it records the PR the merge gate checks.`;
|
|
17470
|
+
case "pr-read":
|
|
17471
|
+
return `workit: ${quote("workit pr status")} shows the PR, its checks and the verdict state; ${quote("workit ci wait")} waits for checks.`;
|
|
17472
|
+
case "merge":
|
|
17473
|
+
return null;
|
|
17474
|
+
}
|
|
17475
|
+
};
|
|
17476
|
+
var actionsIn = (cwd, command, dialect) => {
|
|
17477
|
+
const out = [];
|
|
17478
|
+
const repos = new Map;
|
|
17479
|
+
for (const invocation of rawInvocations(command, dialect)) {
|
|
17480
|
+
if (invocation.dir === undefined)
|
|
17481
|
+
continue;
|
|
17482
|
+
const action = rawAction(invocation);
|
|
17483
|
+
if (!action)
|
|
17484
|
+
continue;
|
|
17485
|
+
const dir = path17.resolve(cwd, invocation.dir ?? ".");
|
|
17486
|
+
if (!repos.has(dir)) {
|
|
17487
|
+
const repo = repoAt(dir);
|
|
17488
|
+
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
17489
|
+
}
|
|
17490
|
+
const repo = repos.get(dir);
|
|
17491
|
+
if (repo)
|
|
17492
|
+
out.push({ action, repo });
|
|
17493
|
+
}
|
|
17494
|
+
return out;
|
|
17495
|
+
};
|
|
17496
|
+
var mentionsRawTool = (command) => /\b(?:git|gh|glab)\b/i.test(command);
|
|
17497
|
+
var dialectOf = (input) => (input.event.kind === "shell.pre" || input.event.kind === "shell.post" ? input.event.dialect : undefined) ?? "posix";
|
|
17498
|
+
function rawGitPre(input, command) {
|
|
17499
|
+
try {
|
|
17500
|
+
if (!mentionsRawTool(command))
|
|
17501
|
+
return NONE3;
|
|
17502
|
+
const nudges = [];
|
|
17503
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
|
|
17504
|
+
if (action.kind === "merge")
|
|
17505
|
+
return deny(refusal(action, null));
|
|
17506
|
+
if (action.kind === "push")
|
|
17507
|
+
for (const target of action.targets) {
|
|
17508
|
+
const branch = target ?? repo.branch;
|
|
17509
|
+
if (branch && guardedBranch(repo.dir, branch) && toPushRemote(repo, action.remote))
|
|
17510
|
+
return deny(refusal(action, branch));
|
|
17511
|
+
}
|
|
17512
|
+
const nudge = nudgeFor(action, input.host, input.session.id);
|
|
17513
|
+
if (nudge && !nudges.includes(nudge))
|
|
17514
|
+
nudges.push(nudge);
|
|
17515
|
+
}
|
|
17516
|
+
return nudges.length ? { kind: "context", text: nudges.join(`
|
|
17517
|
+
`) } : NONE3;
|
|
17518
|
+
} catch {
|
|
17519
|
+
return NONE3;
|
|
17520
|
+
}
|
|
17521
|
+
}
|
|
17522
|
+
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
17523
|
+
var NEXT_COMMAND = "next";
|
|
17524
|
+
var MARKER_TTL_SECONDS = 3600;
|
|
17525
|
+
var markerFile = (repo, session, key) => path17.join(repo.location.dir, "hooks", `commit-${createHash10("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
17526
|
+
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash10("sha256").update(command).digest("hex")}`;
|
|
17527
|
+
function noteRawCommit(input, command, key) {
|
|
17528
|
+
try {
|
|
17529
|
+
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
17530
|
+
return;
|
|
17531
|
+
const at = Math.floor(Date.now() / 1000);
|
|
17532
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
|
|
17533
|
+
if (action.kind !== "commit")
|
|
17534
|
+
continue;
|
|
17535
|
+
const file = markerFile(repo, input.session.id, key);
|
|
17536
|
+
fs12.mkdirSync(path17.dirname(file), { recursive: true });
|
|
17537
|
+
fs12.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
17538
|
+
`);
|
|
17539
|
+
}
|
|
17540
|
+
} catch {}
|
|
17541
|
+
}
|
|
17542
|
+
var headCommit = (dir) => {
|
|
17543
|
+
const run = spawnSync9("git", [
|
|
17544
|
+
"log",
|
|
17545
|
+
"-1",
|
|
17546
|
+
"--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
|
|
17547
|
+
], {
|
|
17548
|
+
cwd: dir,
|
|
17549
|
+
encoding: "utf8",
|
|
17550
|
+
timeout: 5000,
|
|
17551
|
+
windowsHide: true,
|
|
17552
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" }
|
|
17553
|
+
});
|
|
17554
|
+
const [sha, committed, subject = "", trailer = ""] = (run.status === 0 ? run.stdout.trim() : "").split("\x1F");
|
|
17555
|
+
return sha && /^[0-9a-f]{40,64}$/.test(sha) ? { sha, committed: Number(committed), subject, trailer: trailer.trim() || null } : null;
|
|
17556
|
+
};
|
|
17557
|
+
var settle2 = (input, repo, file) => {
|
|
17558
|
+
let marker;
|
|
17559
|
+
try {
|
|
17560
|
+
marker = JSON.parse(fs12.readFileSync(file, "utf8"));
|
|
17561
|
+
} catch {
|
|
17562
|
+
return;
|
|
17563
|
+
}
|
|
17564
|
+
fs12.rmSync(file, { force: true });
|
|
17565
|
+
const session = input.session.id;
|
|
17566
|
+
const now = Date.now() / 1000;
|
|
17567
|
+
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
17568
|
+
return;
|
|
17569
|
+
if (!session || !repo.branch)
|
|
17570
|
+
return;
|
|
17571
|
+
const head = headCommit(repo.dir);
|
|
17572
|
+
if (!head || head.sha === marker.head || head.committed < marker.at)
|
|
17573
|
+
return;
|
|
17574
|
+
if (head.trailer && head.trailer !== session)
|
|
17575
|
+
return;
|
|
17576
|
+
const ledger = readLedger(repo.dir);
|
|
17577
|
+
if (!ledger.ok)
|
|
17578
|
+
return;
|
|
17579
|
+
const claimed = ledger.value.rows.some((row) => row.type === "commit.recorded" && (row.sha === head.sha || row.head === head.sha));
|
|
17580
|
+
if (claimed)
|
|
17581
|
+
return;
|
|
17582
|
+
appendHookObserved(repo.dir, {
|
|
17583
|
+
type: "commit.recorded",
|
|
17584
|
+
actor: { host: input.host, session, agentId: input.session.agentId },
|
|
17585
|
+
branch: repo.branch,
|
|
17586
|
+
head: head.sha,
|
|
17587
|
+
sha: head.sha,
|
|
17588
|
+
session,
|
|
17589
|
+
agentId: input.session.agentId,
|
|
17590
|
+
subject: head.subject.slice(0, 200),
|
|
17591
|
+
files: [],
|
|
17592
|
+
fileCount: null,
|
|
17593
|
+
via: "raw_shell"
|
|
17594
|
+
});
|
|
17595
|
+
};
|
|
17596
|
+
function rawGitPost(input, command, key) {
|
|
17597
|
+
try {
|
|
17598
|
+
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
17599
|
+
return;
|
|
17600
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input)))
|
|
17601
|
+
if (action.kind === "commit")
|
|
17602
|
+
settle2(input, repo, markerFile(repo, input.session.id, key));
|
|
17603
|
+
} catch {}
|
|
17604
|
+
}
|
|
17605
|
+
function settlePendingCommit(input) {
|
|
17606
|
+
try {
|
|
17607
|
+
if (!input.session.id)
|
|
17608
|
+
return;
|
|
17609
|
+
const repo = repoAt(input.cwd);
|
|
17610
|
+
if (!repo)
|
|
17611
|
+
return;
|
|
17612
|
+
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
17613
|
+
if (fs12.existsSync(file))
|
|
17614
|
+
settle2(input, repo, file);
|
|
17615
|
+
} catch {}
|
|
17616
|
+
}
|
|
16885
17617
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
16886
17618
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
16887
|
-
import
|
|
17619
|
+
import path18 from "node:path";
|
|
16888
17620
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16889
17621
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
16890
17622
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
16891
17623
|
var existingDirectory = (value) => {
|
|
16892
|
-
if (!nonEmpty2(value) || !
|
|
17624
|
+
if (!nonEmpty2(value) || !path18.isAbsolute(value) || !existsSync6(value))
|
|
16893
17625
|
return null;
|
|
16894
17626
|
try {
|
|
16895
17627
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -16897,8 +17629,8 @@ var existingDirectory = (value) => {
|
|
|
16897
17629
|
return null;
|
|
16898
17630
|
}
|
|
16899
17631
|
};
|
|
16900
|
-
var
|
|
16901
|
-
var
|
|
17632
|
+
var SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
17633
|
+
var SCRIPT_FLAG2 = /^(?:-\w*c|-command|\/c)$/i;
|
|
16902
17634
|
var quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg);
|
|
16903
17635
|
var commandText = (value) => {
|
|
16904
17636
|
if (nonEmpty2(value))
|
|
@@ -16909,9 +17641,9 @@ var commandText = (value) => {
|
|
|
16909
17641
|
if (!argv.every((arg) => typeof arg === "string"))
|
|
16910
17642
|
return null;
|
|
16911
17643
|
const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
|
|
16912
|
-
if (
|
|
17644
|
+
if (SHELLS2.has(shell))
|
|
16913
17645
|
for (let index = 1;index < argv.length - 1; index++) {
|
|
16914
|
-
if (
|
|
17646
|
+
if (SCRIPT_FLAG2.test(argv[index]))
|
|
16915
17647
|
return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
|
|
16916
17648
|
if (!/^[-/]/.test(argv[index]))
|
|
16917
17649
|
break;
|
|
@@ -16952,7 +17684,7 @@ var writePaths = (input) => {
|
|
|
16952
17684
|
};
|
|
16953
17685
|
// packages/workit-core/src/hooks/handle.ts
|
|
16954
17686
|
var usable2 = (value) => value === "native" || value === "partial";
|
|
16955
|
-
var
|
|
17687
|
+
var NONE4 = { kind: "none" };
|
|
16956
17688
|
var offered = new Set;
|
|
16957
17689
|
var CLAUDE_WORKTREE_IMPLEMENTER = "workit:implementer";
|
|
16958
17690
|
var CLAUDE_JUDGES = new Set(["workit:verifier", "workit:reviewer"]);
|
|
@@ -16971,7 +17703,7 @@ function handleHook(input, deps) {
|
|
|
16971
17703
|
switch (event.kind) {
|
|
16972
17704
|
case "session.start": {
|
|
16973
17705
|
if (!usable2(descriptor.context.sessionStart))
|
|
16974
|
-
return
|
|
17706
|
+
return NONE4;
|
|
16975
17707
|
const key = input.session.id ? `${input.host}\x00${input.session.id}` : null;
|
|
16976
17708
|
const offer = event.source === "startup" && (key === null || !offered.has(key));
|
|
16977
17709
|
if (offer && key !== null)
|
|
@@ -16986,19 +17718,34 @@ function handleHook(input, deps) {
|
|
|
16986
17718
|
}
|
|
16987
17719
|
case "context.turn": {
|
|
16988
17720
|
if (!usable2(descriptor.context.perTurn))
|
|
16989
|
-
return
|
|
17721
|
+
return NONE4;
|
|
16990
17722
|
const text = turnContextText(input, descriptor);
|
|
16991
|
-
return text ? { kind: "context", text } :
|
|
17723
|
+
return text ? { kind: "context", text } : NONE4;
|
|
16992
17724
|
}
|
|
16993
17725
|
case "shell.pre": {
|
|
16994
|
-
const
|
|
16995
|
-
|
|
17726
|
+
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
17727
|
+
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
|
|
17728
|
+
if (policy.kind !== "none")
|
|
16996
17729
|
return policy;
|
|
16997
|
-
const
|
|
16998
|
-
|
|
17730
|
+
const postTool = usable2(descriptor.events["shell.post"].support);
|
|
17731
|
+
if (!postTool)
|
|
17732
|
+
settlePendingCommit(input);
|
|
17733
|
+
const raw = canDeny ? rawGitPre(input, event.command) : NONE4;
|
|
17734
|
+
if (raw.kind === "deny")
|
|
17735
|
+
return raw;
|
|
17736
|
+
const writes = usable2(descriptor.events["write.pre"].support) ? shellWrites(event.command) : { writes: false, targets: null };
|
|
17737
|
+
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE4;
|
|
17738
|
+
if (gate.kind === "deny")
|
|
17739
|
+
return gate;
|
|
17740
|
+
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
17741
|
+
return raw;
|
|
16999
17742
|
}
|
|
17743
|
+
case "shell.post":
|
|
17744
|
+
if (usable2(descriptor.events["shell.post"].support))
|
|
17745
|
+
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
17746
|
+
return NONE4;
|
|
17000
17747
|
case "write.pre":
|
|
17001
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) :
|
|
17748
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE4;
|
|
17002
17749
|
case "subagent.start":
|
|
17003
17750
|
return {
|
|
17004
17751
|
kind: "context",
|
|
@@ -17010,19 +17757,18 @@ function handleHook(input, deps) {
|
|
|
17010
17757
|
userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
|
|
17011
17758
|
};
|
|
17012
17759
|
case "tool.pre":
|
|
17013
|
-
case "shell.post":
|
|
17014
17760
|
case "subagent.stop":
|
|
17015
17761
|
case "prompt.submit":
|
|
17016
17762
|
case "stop":
|
|
17017
|
-
return
|
|
17763
|
+
return NONE4;
|
|
17018
17764
|
}
|
|
17019
17765
|
}
|
|
17020
17766
|
var failureDecision = (descriptor, event, error) => {
|
|
17021
17767
|
if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
|
|
17022
|
-
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } :
|
|
17768
|
+
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE4;
|
|
17023
17769
|
if (event === "session.start" || event === "subagent.start")
|
|
17024
17770
|
return { kind: "context", text: `[workit diagnostic: ${error}]` };
|
|
17025
|
-
return
|
|
17771
|
+
return NONE4;
|
|
17026
17772
|
};
|
|
17027
17773
|
var dispatchHook = (adapter, raw, env = process.env) => {
|
|
17028
17774
|
const parsed = adapter.parse(raw, env);
|
|
@@ -17145,6 +17891,7 @@ var EVENTS = {
|
|
|
17145
17891
|
};
|
|
17146
17892
|
var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
17147
17893
|
var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
|
|
17894
|
+
var dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix";
|
|
17148
17895
|
var toolCommand = (value) => isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
|
|
17149
17896
|
var eventOf = (name, value) => {
|
|
17150
17897
|
const toolUseId = optionalText(value.tool_use_id);
|
|
@@ -17169,7 +17916,7 @@ var eventOf = (name, value) => {
|
|
|
17169
17916
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
17170
17917
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
17171
17918
|
const command = toolCommand(value);
|
|
17172
|
-
return command ? { ok: true, event: { kind: "shell.pre", command, toolUseId } } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
|
|
17919
|
+
return command ? { ok: true, event: { kind: "shell.pre", command, toolUseId, dialect: dialectOf2(value) } } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
|
|
17173
17920
|
}
|
|
17174
17921
|
case "PostToolUse": {
|
|
17175
17922
|
const command = toolCommand(value);
|
|
@@ -17183,7 +17930,8 @@ var eventOf = (name, value) => {
|
|
|
17183
17930
|
command,
|
|
17184
17931
|
stdout: optionalText(response.stdout) ?? "",
|
|
17185
17932
|
exitCode: typeof response.exit_code === "number" ? response.exit_code : null,
|
|
17186
|
-
toolUseId
|
|
17933
|
+
toolUseId,
|
|
17934
|
+
dialect: dialectOf2(value)
|
|
17187
17935
|
}
|
|
17188
17936
|
};
|
|
17189
17937
|
}
|
|
@@ -17224,7 +17972,13 @@ var eventOf = (name, value) => {
|
|
|
17224
17972
|
};
|
|
17225
17973
|
}
|
|
17226
17974
|
};
|
|
17227
|
-
var CONTEXT_EVENTS = new Set([
|
|
17975
|
+
var CONTEXT_EVENTS = new Set([
|
|
17976
|
+
"SessionStart",
|
|
17977
|
+
"UserPromptSubmit",
|
|
17978
|
+
"SubagentStart",
|
|
17979
|
+
"PreToolUse",
|
|
17980
|
+
"PostToolUse"
|
|
17981
|
+
]);
|
|
17228
17982
|
var render = (decision, native) => {
|
|
17229
17983
|
if (decision.kind === "deny" && native === "PreToolUse")
|
|
17230
17984
|
return {
|
|
@@ -17306,7 +18060,7 @@ var CODEX_DESCRIPTOR = {
|
|
|
17306
18060
|
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
17307
18061
|
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
17308
18062
|
"write.pre": { support: "undocumented", native: null },
|
|
17309
|
-
"shell.post":
|
|
18063
|
+
"shell.post": { support: "native", native: "PostToolUse" },
|
|
17310
18064
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
17311
18065
|
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
17312
18066
|
"prompt.submit": undocumented,
|
|
@@ -17327,7 +18081,7 @@ var CODEX_DESCRIPTOR = {
|
|
|
17327
18081
|
worktreeIsolation: "undocumented",
|
|
17328
18082
|
maxConcurrency: "undocumented"
|
|
17329
18083
|
},
|
|
17330
|
-
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "
|
|
18084
|
+
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "native" },
|
|
17331
18085
|
interaction: { questions: "none", writeBoundary: "partial" },
|
|
17332
18086
|
stopControl: "undocumented",
|
|
17333
18087
|
shellAvailable: "native",
|
|
@@ -17393,6 +18147,7 @@ var CODEX_DESCRIPTOR = {
|
|
|
17393
18147
|
var EVENTS2 = {
|
|
17394
18148
|
SessionStart: "session.start",
|
|
17395
18149
|
PreToolUse: "shell.pre",
|
|
18150
|
+
PostToolUse: "shell.post",
|
|
17396
18151
|
SubagentStart: "subagent.start",
|
|
17397
18152
|
SubagentStop: "subagent.stop"
|
|
17398
18153
|
};
|
|
@@ -17406,10 +18161,11 @@ var parseCodexHookInput = (value) => {
|
|
|
17406
18161
|
const cwd = existingDirectory(value.cwd);
|
|
17407
18162
|
if (!cwd)
|
|
17408
18163
|
return { ok: false, error: "cwd must be an existing absolute directory" };
|
|
17409
|
-
|
|
18164
|
+
const toolEvent = event === "PreToolUse" || event === "PostToolUse";
|
|
18165
|
+
if (toolEvent && !nonEmpty2(value.tool_name))
|
|
17410
18166
|
return { ok: false, error: "tool_name is required" };
|
|
17411
18167
|
const command = isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
|
|
17412
|
-
if (
|
|
18168
|
+
if (toolEvent && isShellTool(value.tool_name) && !command)
|
|
17413
18169
|
return { ok: false, error: "tool_input.command is required for shell tools" };
|
|
17414
18170
|
if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
|
|
17415
18171
|
return { ok: false, error: "agent_id is required" };
|
|
@@ -17425,7 +18181,8 @@ var parseCodexHookInput = (value) => {
|
|
|
17425
18181
|
...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
|
|
17426
18182
|
...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
|
|
17427
18183
|
...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
|
|
17428
|
-
...
|
|
18184
|
+
...toolEvent ? { tool_input: value.tool_input } : {},
|
|
18185
|
+
...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
|
|
17429
18186
|
...command ? { command } : {},
|
|
17430
18187
|
...nonEmpty2(value.tool_use_id) ? { tool_use_id: value.tool_use_id } : {},
|
|
17431
18188
|
...nonEmpty2(value.agent_id) ? { agent_id: value.agent_id } : {},
|
|
@@ -17450,6 +18207,16 @@ var protocolEvent = (input) => {
|
|
|
17450
18207
|
toolUseId
|
|
17451
18208
|
} : { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
17452
18209
|
}
|
|
18210
|
+
case "PostToolUse": {
|
|
18211
|
+
const toolUseId = input.tool_use_id ?? null;
|
|
18212
|
+
return isShellTool(input.tool_name) ? {
|
|
18213
|
+
kind: "shell.post",
|
|
18214
|
+
command: input.command ?? "",
|
|
18215
|
+
stdout: input.tool_response ?? "",
|
|
18216
|
+
exitCode: null,
|
|
18217
|
+
toolUseId
|
|
18218
|
+
} : { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
18219
|
+
}
|
|
17453
18220
|
case "SubagentStart":
|
|
17454
18221
|
return {
|
|
17455
18222
|
kind: "subagent.start",
|
|
@@ -17514,11 +18281,11 @@ var codexAdapter = {
|
|
|
17514
18281
|
};
|
|
17515
18282
|
},
|
|
17516
18283
|
render: render2,
|
|
17517
|
-
addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. A verifier or reviewer records workit ledger verdict under a session the lead assigns (WORKIT_SESSION_ID=<lead>-v<n>), never the author's. Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
|
|
18284
|
+
addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. workit acts as this thread (CODEX_THREAD_ID${input.session.id ? `=${input.session.id}` : ""}) unless WORKIT_SESSION_ID is set. A verifier or reviewer records workit ledger verdict under a session the lead assigns (WORKIT_HOST=codex_cli WORKIT_SESSION_ID=<lead>-v<n>), never the author's. Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
|
|
17518
18285
|
};
|
|
17519
18286
|
|
|
17520
18287
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
17521
|
-
import
|
|
18288
|
+
import path19 from "node:path";
|
|
17522
18289
|
var undocumented2 = { support: "undocumented", native: null };
|
|
17523
18290
|
var none = { support: "none", native: null };
|
|
17524
18291
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -17699,7 +18466,7 @@ var protocolEvent2 = (input) => {
|
|
|
17699
18466
|
return { kind: "compact.pre", trigger: "auto" };
|
|
17700
18467
|
}
|
|
17701
18468
|
};
|
|
17702
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
18469
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path19.sep) ? root : `${root}${path19.sep}`);
|
|
17703
18470
|
var hookCwd = (input) => {
|
|
17704
18471
|
const cwd = existingDirectory(input.cwd);
|
|
17705
18472
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
@@ -17722,6 +18489,8 @@ var render3 = (decision, native) => {
|
|
|
17722
18489
|
json: cursorDeny(decision.reason),
|
|
17723
18490
|
exitCode: native !== null && BLOCKING.has(native) ? 2 : 0
|
|
17724
18491
|
};
|
|
18492
|
+
if (decision.kind === "context" && native === "beforeShellExecution")
|
|
18493
|
+
return { json: { permission: "allow", agent_message: decision.text }, exitCode: 0 };
|
|
17725
18494
|
if (decision.kind === "context")
|
|
17726
18495
|
return { json: { additional_context: decision.text }, exitCode: 0 };
|
|
17727
18496
|
if (decision.kind === "notice")
|
|
@@ -17758,7 +18527,8 @@ var cursorAdapter = {
|
|
|
17758
18527
|
}
|
|
17759
18528
|
};
|
|
17760
18529
|
},
|
|
17761
|
-
render: render3
|
|
18530
|
+
render: render3,
|
|
18531
|
+
addendum: (input) => input.session.id ? `<workit-session>Cursor puts no session id in the shell: prefix workit commands that record authorship with WORKIT_SESSION_ID=${input.session.id} (e.g. \`WORKIT_SESSION_ID=${input.session.id} workit git commit -m …\`). A verifier uses its own id, never this one.</workit-session>` : null
|
|
17762
18532
|
};
|
|
17763
18533
|
|
|
17764
18534
|
// packages/workit-core/src/hooks/run.ts
|