@brainervirus/workit-cursor 8.1.1 → 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 +798 -84
- package/dist/mcp-server.js +83 -4
- package/dist/workit-hook.js +799 -58
- package/package.json +3 -3
- package/skills/workit-ship/SKILL.md +14 -14
|
@@ -8572,9 +8572,27 @@ function patchId(cwd, base, head) {
|
|
|
8572
8572
|
const id = git(cwd, ["patch-id", "--stable"], { input: diff.stdout });
|
|
8573
8573
|
return line(id)?.split(/\s+/u)[0] ?? null;
|
|
8574
8574
|
}
|
|
8575
|
+
function remoteNames(cwd) {
|
|
8576
|
+
return (git(cwd, ["remote"]).stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
|
|
8577
|
+
}
|
|
8575
8578
|
function currentBranch(cwd) {
|
|
8576
8579
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
8577
8580
|
}
|
|
8581
|
+
function pushRemoteName(cwd, branch) {
|
|
8582
|
+
const name = branch ?? currentBranch(cwd);
|
|
8583
|
+
const remotes = remoteNames(cwd);
|
|
8584
|
+
const candidates = [
|
|
8585
|
+
name ? config2(cwd, `branch.${name}.pushRemote`) : null,
|
|
8586
|
+
config2(cwd, "remote.pushDefault"),
|
|
8587
|
+
name ? config2(cwd, `branch.${name}.remote`) : null
|
|
8588
|
+
];
|
|
8589
|
+
for (const candidate of candidates)
|
|
8590
|
+
if (candidate && candidate !== "." && remotes.includes(candidate))
|
|
8591
|
+
return candidate;
|
|
8592
|
+
if (remotes.includes("origin"))
|
|
8593
|
+
return "origin";
|
|
8594
|
+
return remotes.length === 1 ? remotes[0] : null;
|
|
8595
|
+
}
|
|
8578
8596
|
var MAX_BUFFER, GIT_TIMEOUTS, git = (cwd, args, options = {}) => {
|
|
8579
8597
|
const result = spawnSync("git", args, {
|
|
8580
8598
|
cwd,
|
|
@@ -8603,13 +8621,44 @@ var MAX_BUFFER, GIT_TIMEOUTS, git = (cwd, args, options = {}) => {
|
|
|
8603
8621
|
const { atimeNs, mtimeNs } = fs.statSync(from, { bigint: true });
|
|
8604
8622
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
8605
8623
|
fs.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
8606
|
-
};
|
|
8624
|
+
}, config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
8607
8625
|
var init_rev = __esm(() => {
|
|
8608
8626
|
MAX_BUFFER = 256 * 1024 * 1024;
|
|
8609
8627
|
GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
8610
8628
|
MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
8611
8629
|
});
|
|
8612
8630
|
|
|
8631
|
+
// packages/workit-core/src/host-session.ts
|
|
8632
|
+
function hostSessionFromEnv(env) {
|
|
8633
|
+
const value = (key) => env[key]?.trim() || null;
|
|
8634
|
+
let inner = null;
|
|
8635
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
8636
|
+
const session = value(key);
|
|
8637
|
+
if (session) {
|
|
8638
|
+
inner = { host: host(env), session };
|
|
8639
|
+
break;
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
const declared = value("WORKIT_HOST");
|
|
8643
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
8644
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
8645
|
+
if (!inherited)
|
|
8646
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
8647
|
+
}
|
|
8648
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
8649
|
+
}
|
|
8650
|
+
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
8651
|
+
var init_host_session = __esm(() => {
|
|
8652
|
+
HOST_SESSION_VARS = [
|
|
8653
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
8654
|
+
["PI_SESSION_ID", () => "pi"],
|
|
8655
|
+
[
|
|
8656
|
+
"CODEX_THREAD_ID",
|
|
8657
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
8658
|
+
]
|
|
8659
|
+
];
|
|
8660
|
+
});
|
|
8661
|
+
|
|
8613
8662
|
// packages/workit-core/src/store/paths.ts
|
|
8614
8663
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
8615
8664
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -8952,6 +9001,74 @@ function ledgerPath(cwd) {
|
|
|
8952
9001
|
const root = storeRoot(cwd);
|
|
8953
9002
|
return root.ok ? { ok: true, value: path3.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
8954
9003
|
}
|
|
9004
|
+
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
9005
|
+
const lock = `${file}.lock`;
|
|
9006
|
+
const deadline = Date.now() + waitMs;
|
|
9007
|
+
let token = null;
|
|
9008
|
+
for (;; ) {
|
|
9009
|
+
token = ledgerLock.acquire(lock);
|
|
9010
|
+
if (token)
|
|
9011
|
+
break;
|
|
9012
|
+
const observed = lockToken(lock);
|
|
9013
|
+
const age = lockAgeMs(lock);
|
|
9014
|
+
if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
|
|
9015
|
+
continue;
|
|
9016
|
+
if (Date.now() >= deadline)
|
|
9017
|
+
return null;
|
|
9018
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
9019
|
+
}
|
|
9020
|
+
try {
|
|
9021
|
+
return fn();
|
|
9022
|
+
} finally {
|
|
9023
|
+
ledgerLock.release(lock, token);
|
|
9024
|
+
}
|
|
9025
|
+
}
|
|
9026
|
+
function appendLine(file, line, options) {
|
|
9027
|
+
const write = () => {
|
|
9028
|
+
const fd = fs3.openSync(file, "a+");
|
|
9029
|
+
try {
|
|
9030
|
+
const size = fs3.fstatSync(fd).size;
|
|
9031
|
+
let fence = "";
|
|
9032
|
+
if (size > 0) {
|
|
9033
|
+
const last = Buffer.alloc(1);
|
|
9034
|
+
fs3.readSync(fd, last, 0, 1, size - 1);
|
|
9035
|
+
if (last[0] !== 10)
|
|
9036
|
+
fence = `
|
|
9037
|
+
`;
|
|
9038
|
+
}
|
|
9039
|
+
const bytes = Buffer.from(fence + line);
|
|
9040
|
+
const written = fs3.writeSync(fd, bytes);
|
|
9041
|
+
if (written !== bytes.length)
|
|
9042
|
+
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
9043
|
+
return { ok: true, value: undefined };
|
|
9044
|
+
} finally {
|
|
9045
|
+
fs3.closeSync(fd);
|
|
9046
|
+
}
|
|
9047
|
+
};
|
|
9048
|
+
try {
|
|
9049
|
+
fs3.mkdirSync(path3.dirname(file), { recursive: true });
|
|
9050
|
+
if (!options.forceLock && !onNetworkFs(path3.dirname(file)))
|
|
9051
|
+
return write();
|
|
9052
|
+
const locked = withLedgerLock(file, write);
|
|
9053
|
+
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
9054
|
+
} catch (error) {
|
|
9055
|
+
return err("unavailable", `cannot append to ${file}: ${error.message}`);
|
|
9056
|
+
}
|
|
9057
|
+
}
|
|
9058
|
+
function appendRaw(cwd, row, options) {
|
|
9059
|
+
const { full, line } = serialize(row, options.now ?? new Date);
|
|
9060
|
+
const bytes = Buffer.byteLength(line);
|
|
9061
|
+
if (bytes > MAX_LINE_BYTES)
|
|
9062
|
+
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>`);
|
|
9063
|
+
const file = ledgerPath(cwd);
|
|
9064
|
+
if (!file.ok)
|
|
9065
|
+
return file;
|
|
9066
|
+
const written = appendLine(file.value, line, options);
|
|
9067
|
+
return written.ok ? { ok: true, value: full } : written;
|
|
9068
|
+
}
|
|
9069
|
+
function appendHookObserved(cwd, row) {
|
|
9070
|
+
return appendRaw(cwd, { ...row, observer: "host_hook" }, {});
|
|
9071
|
+
}
|
|
8955
9072
|
function supersedeAllowed(by, target) {
|
|
8956
9073
|
if (by.type !== target.type)
|
|
8957
9074
|
return `a ${by.type} row cannot supersede a ${target.type} row`;
|
|
@@ -9169,7 +9286,7 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
9169
9286
|
authors: [...authors].toSorted()
|
|
9170
9287
|
};
|
|
9171
9288
|
}
|
|
9172
|
-
var LEDGER_VERSION = 1, PASSING, FAILING, STRONG_RESULTS, PR_ROW_TYPES, err = (code, error, unblock) => ({
|
|
9289
|
+
var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, PASSING, FAILING, STRONG_RESULTS, PR_ROW_TYPES, err = (code, error, unblock) => ({
|
|
9173
9290
|
ok: false,
|
|
9174
9291
|
code,
|
|
9175
9292
|
error,
|
|
@@ -9199,7 +9316,32 @@ var LEDGER_VERSION = 1, PASSING, FAILING, STRONG_RESULTS, PR_ROW_TYPES, err = (c
|
|
|
9199
9316
|
const run = gitRun2(cwd, args);
|
|
9200
9317
|
const value = run.ok ? run.stdout.trim() : "";
|
|
9201
9318
|
return value ? value : null;
|
|
9202
|
-
}, safeRef = (value) => value.length > 0 && !value.startsWith("-"),
|
|
9319
|
+
}, safeRef = (value) => value.length > 0 && !value.startsWith("-"), newId2 = () => `${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`, NETWORK_FS_TYPES, isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0), onNetworkFs = (dir) => {
|
|
9320
|
+
if (process.platform !== "linux")
|
|
9321
|
+
return false;
|
|
9322
|
+
try {
|
|
9323
|
+
return isNetworkFsType(fs3.statfsSync(dir).type);
|
|
9324
|
+
} catch {
|
|
9325
|
+
return false;
|
|
9326
|
+
}
|
|
9327
|
+
}, LOCK_WAIT_MS = 5000, LOCK_STALE_MS = 30000, LOCK_OWNER = "owner", lockToken = (lock) => {
|
|
9328
|
+
try {
|
|
9329
|
+
return fs3.readFileSync(path3.join(lock, LOCK_OWNER), "utf8");
|
|
9330
|
+
} catch {
|
|
9331
|
+
return null;
|
|
9332
|
+
}
|
|
9333
|
+
}, lockAgeMs = (lock) => {
|
|
9334
|
+
for (const target of [path3.join(lock, LOCK_OWNER), lock]) {
|
|
9335
|
+
try {
|
|
9336
|
+
return Date.now() - fs3.statSync(target).mtimeMs;
|
|
9337
|
+
} catch {}
|
|
9338
|
+
}
|
|
9339
|
+
return null;
|
|
9340
|
+
}, ledgerLock, serialize = (row, now) => {
|
|
9341
|
+
const full = { v: LEDGER_VERSION, id: newId2(), at: now.toISOString(), ...row };
|
|
9342
|
+
return { full, line: `${JSON.stringify(full)}
|
|
9343
|
+
` };
|
|
9344
|
+
}, isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), str = (value) => typeof value === "string" ? value : null, normalizeActor = (value) => {
|
|
9203
9345
|
const actor = isRecord(value) ? value : {};
|
|
9204
9346
|
return {
|
|
9205
9347
|
host: str(actor.host) ?? "unknown",
|
|
@@ -9218,6 +9360,7 @@ var LEDGER_VERSION = 1, PASSING, FAILING, STRONG_RESULTS, PR_ROW_TYPES, err = (c
|
|
|
9218
9360
|
}, OBSERVED_ONLY;
|
|
9219
9361
|
var init_ledger = __esm(() => {
|
|
9220
9362
|
init_rev();
|
|
9363
|
+
init_host_session();
|
|
9221
9364
|
init_paths();
|
|
9222
9365
|
PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
9223
9366
|
FAILING = new Set(["failed", "blocked"]);
|
|
@@ -9235,6 +9378,57 @@ var init_ledger = __esm(() => {
|
|
|
9235
9378
|
1196443219,
|
|
9236
9379
|
198183888
|
|
9237
9380
|
]);
|
|
9381
|
+
ledgerLock = {
|
|
9382
|
+
acquire(lock) {
|
|
9383
|
+
try {
|
|
9384
|
+
fs3.mkdirSync(lock);
|
|
9385
|
+
} catch (error) {
|
|
9386
|
+
if (error.code === "EEXIST")
|
|
9387
|
+
return null;
|
|
9388
|
+
throw error;
|
|
9389
|
+
}
|
|
9390
|
+
const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
|
|
9391
|
+
fs3.writeFileSync(path3.join(lock, LOCK_OWNER), token);
|
|
9392
|
+
return token;
|
|
9393
|
+
},
|
|
9394
|
+
release(lock, token) {
|
|
9395
|
+
if (lockToken(lock) !== token)
|
|
9396
|
+
return false;
|
|
9397
|
+
const tomb = `${lock}.release-${randomBytes(6).toString("hex")}`;
|
|
9398
|
+
try {
|
|
9399
|
+
fs3.renameSync(lock, tomb);
|
|
9400
|
+
} catch {
|
|
9401
|
+
return false;
|
|
9402
|
+
}
|
|
9403
|
+
if (lockToken(tomb) !== token) {
|
|
9404
|
+
try {
|
|
9405
|
+
fs3.renameSync(tomb, lock);
|
|
9406
|
+
} catch {}
|
|
9407
|
+
return false;
|
|
9408
|
+
}
|
|
9409
|
+
fs3.rmSync(tomb, { recursive: true, force: true });
|
|
9410
|
+
return true;
|
|
9411
|
+
},
|
|
9412
|
+
token: lockToken,
|
|
9413
|
+
reap(lock, observed) {
|
|
9414
|
+
const tomb = `${lock}.reap-${randomBytes(6).toString("hex")}`;
|
|
9415
|
+
try {
|
|
9416
|
+
fs3.renameSync(lock, tomb);
|
|
9417
|
+
} catch {
|
|
9418
|
+
return false;
|
|
9419
|
+
}
|
|
9420
|
+
if (lockToken(tomb) === observed) {
|
|
9421
|
+
fs3.rmSync(tomb, { recursive: true, force: true });
|
|
9422
|
+
return true;
|
|
9423
|
+
}
|
|
9424
|
+
try {
|
|
9425
|
+
fs3.renameSync(tomb, lock);
|
|
9426
|
+
} catch {
|
|
9427
|
+
fs3.rmSync(tomb, { recursive: true, force: true });
|
|
9428
|
+
}
|
|
9429
|
+
return false;
|
|
9430
|
+
}
|
|
9431
|
+
};
|
|
9238
9432
|
OBSERVED_ONLY = new Set(["check", "merge.unverified", ...PR_ROW_TYPES]);
|
|
9239
9433
|
});
|
|
9240
9434
|
|
|
@@ -10436,7 +10630,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
10436
10630
|
profiles: record(nonBlank, profileSchema).optional()
|
|
10437
10631
|
}).passthrough();
|
|
10438
10632
|
return entrySchema;
|
|
10439
|
-
}, runtimeWorkspaceIndexSchema, matchRuntimeWorkspace = (cwd) => {
|
|
10633
|
+
}, resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs"), runtimeWorkspaceIndexSchema, matchRuntimeWorkspace = (cwd) => {
|
|
10440
10634
|
const file = path6.join(configDir(), "workspaces.json");
|
|
10441
10635
|
let raw;
|
|
10442
10636
|
try {
|
|
@@ -13533,14 +13727,16 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
13533
13727
|
heldInProcess.delete(lock);
|
|
13534
13728
|
}, envActor = () => {
|
|
13535
13729
|
const value = (key) => process.env[key]?.trim() || null;
|
|
13730
|
+
const acting = hostSessionFromEnv(process.env);
|
|
13536
13731
|
return {
|
|
13537
|
-
host:
|
|
13538
|
-
session:
|
|
13732
|
+
host: acting.host ?? "runtime",
|
|
13733
|
+
session: acting.session,
|
|
13539
13734
|
agentId: value("WORKIT_AGENT_ID")
|
|
13540
13735
|
};
|
|
13541
13736
|
};
|
|
13542
13737
|
var init_task_store = __esm(() => {
|
|
13543
13738
|
init_file_lock();
|
|
13739
|
+
init_host_session();
|
|
13544
13740
|
init_package_root();
|
|
13545
13741
|
init_task_contract();
|
|
13546
13742
|
init_store_lock();
|
|
@@ -16620,7 +16816,7 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
16620
16816
|
error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
|
|
16621
16817
|
};
|
|
16622
16818
|
}
|
|
16623
|
-
};
|
|
16819
|
+
}, resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
16624
16820
|
var init_branch = __esm(() => {
|
|
16625
16821
|
init_branch_policy();
|
|
16626
16822
|
init_commit_flavors();
|
|
@@ -16658,52 +16854,40 @@ var init_policy = __esm(() => {
|
|
|
16658
16854
|
init_route_intent();
|
|
16659
16855
|
});
|
|
16660
16856
|
|
|
16661
|
-
// packages/workit-core/src/hooks/
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
16669
|
-
|
|
16670
|
-
|
|
16671
|
-
|
|
16672
|
-
|
|
16673
|
-
|
|
16674
|
-
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
16675
|
-
unblock: gate.blockers[0].reason
|
|
16676
|
-
};
|
|
16677
|
-
} catch {
|
|
16678
|
-
return NONE2;
|
|
16679
|
-
}
|
|
16680
|
-
}
|
|
16681
|
-
var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
16682
|
-
let rest = "";
|
|
16683
|
-
for (let dir = path16.resolve(file);; dir = path16.dirname(dir)) {
|
|
16684
|
-
try {
|
|
16685
|
-
return path16.join(realpathSync5(dir), rest);
|
|
16686
|
-
} catch {
|
|
16687
|
-
if (path16.dirname(dir) === dir)
|
|
16688
|
-
return path16.resolve(file);
|
|
16689
|
-
rest = rest ? path16.join(path16.basename(dir), rest) : path16.basename(dir);
|
|
16857
|
+
// packages/workit-core/src/hooks/shell-words.ts
|
|
16858
|
+
var closingQuote = (command, start, dialect) => {
|
|
16859
|
+
const quote = command[start];
|
|
16860
|
+
for (let index = start + 1;index < command.length; index++) {
|
|
16861
|
+
const char = command[index];
|
|
16862
|
+
if (quote === "'") {
|
|
16863
|
+
if (char !== "'")
|
|
16864
|
+
continue;
|
|
16865
|
+
if (dialect === "powershell" && command[index + 1] === "'") {
|
|
16866
|
+
index++;
|
|
16867
|
+
continue;
|
|
16868
|
+
}
|
|
16869
|
+
return index;
|
|
16690
16870
|
}
|
|
16871
|
+
if (dialect === "posix" && char === "\\" || dialect === "powershell" && char === "`") {
|
|
16872
|
+
index++;
|
|
16873
|
+
continue;
|
|
16874
|
+
}
|
|
16875
|
+
if (char === '"')
|
|
16876
|
+
return index;
|
|
16691
16877
|
}
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
const segments = posix.split("/");
|
|
16699
|
-
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
16700
|
-
}, segmentsOf = (command) => {
|
|
16878
|
+
return -1;
|
|
16879
|
+
}, unquote = (body, quote, dialect) => {
|
|
16880
|
+
if (quote === "'")
|
|
16881
|
+
return dialect === "powershell" ? body.replaceAll("''", "'") : body;
|
|
16882
|
+
return dialect === "posix" ? body.replace(/\\(["\\$`])/g, "$1") : body.replace(/`(.)/g, "$1");
|
|
16883
|
+
}, segmentsOf = (command, dialect = "posix") => {
|
|
16701
16884
|
const segments = [];
|
|
16702
16885
|
let current = { words: [], redirects: [] };
|
|
16703
16886
|
let word = "";
|
|
16704
16887
|
let inWord = false;
|
|
16705
16888
|
let pending = null;
|
|
16706
16889
|
const heredocs = [];
|
|
16890
|
+
const escape = dialect === "posix" ? "\\" : "`";
|
|
16707
16891
|
const flushWord = () => {
|
|
16708
16892
|
if (!inWord)
|
|
16709
16893
|
return;
|
|
@@ -16727,11 +16911,11 @@ var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
16727
16911
|
for (let index = 0;index < command.length; index++) {
|
|
16728
16912
|
const char = command[index];
|
|
16729
16913
|
if (char === "'" || char === '"') {
|
|
16730
|
-
const end = command
|
|
16731
|
-
word += command.slice(index + 1, end < 0 ? undefined : end);
|
|
16914
|
+
const end = closingQuote(command, index, dialect);
|
|
16915
|
+
word += unquote(command.slice(index + 1, end < 0 ? undefined : end), char, dialect);
|
|
16732
16916
|
inWord = true;
|
|
16733
16917
|
index = end < 0 ? command.length : end;
|
|
16734
|
-
} else if (char ===
|
|
16918
|
+
} else if (char === escape && index + 1 < command.length) {
|
|
16735
16919
|
word += command[++index];
|
|
16736
16920
|
inWord = true;
|
|
16737
16921
|
} else if (char === `
|
|
@@ -16748,7 +16932,14 @@ var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
16748
16932
|
}
|
|
16749
16933
|
} else if (/\s/.test(char))
|
|
16750
16934
|
flushWord();
|
|
16751
|
-
else if ("
|
|
16935
|
+
else if (char === "(" || char === ")") {
|
|
16936
|
+
if (char === "(" && inWord && word.endsWith("$")) {
|
|
16937
|
+
word = word.slice(0, -1);
|
|
16938
|
+
inWord = word !== "";
|
|
16939
|
+
}
|
|
16940
|
+
flushSegment();
|
|
16941
|
+
segments.push({ words: [], redirects: [], group: char === "(" ? "open" : "close" });
|
|
16942
|
+
} else if (";&|".includes(char)) {
|
|
16752
16943
|
if (char === "&" && command[index + 1] === ">")
|
|
16753
16944
|
continue;
|
|
16754
16945
|
flushSegment();
|
|
@@ -16766,7 +16957,7 @@ var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
16766
16957
|
inWord = true;
|
|
16767
16958
|
}
|
|
16768
16959
|
pending = "redirect";
|
|
16769
|
-
} else if (char === "<") {
|
|
16960
|
+
} else if (char === "<" && dialect === "posix") {
|
|
16770
16961
|
flushWord();
|
|
16771
16962
|
if (command[index + 1] === "<") {
|
|
16772
16963
|
index++;
|
|
@@ -16782,6 +16973,47 @@ var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
16782
16973
|
}
|
|
16783
16974
|
flushSegment();
|
|
16784
16975
|
return segments;
|
|
16976
|
+
};
|
|
16977
|
+
|
|
16978
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
16979
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
16980
|
+
import path16 from "node:path";
|
|
16981
|
+
function writeGate(cwd, paths) {
|
|
16982
|
+
try {
|
|
16983
|
+
const gate = gateFor(cwd);
|
|
16984
|
+
if (!gate || gate.blockers.length === 0)
|
|
16985
|
+
return NONE2;
|
|
16986
|
+
if (paths && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
16987
|
+
return NONE2;
|
|
16988
|
+
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
16989
|
+
return {
|
|
16990
|
+
kind: "deny",
|
|
16991
|
+
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
16992
|
+
unblock: gate.blockers[0].reason
|
|
16993
|
+
};
|
|
16994
|
+
} catch {
|
|
16995
|
+
return NONE2;
|
|
16996
|
+
}
|
|
16997
|
+
}
|
|
16998
|
+
var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
16999
|
+
let rest = "";
|
|
17000
|
+
for (let dir = path16.resolve(file);; dir = path16.dirname(dir)) {
|
|
17001
|
+
try {
|
|
17002
|
+
return path16.join(realpathSync5(dir), rest);
|
|
17003
|
+
} catch {
|
|
17004
|
+
if (path16.dirname(dir) === dir)
|
|
17005
|
+
return path16.resolve(file);
|
|
17006
|
+
rest = rest ? path16.join(path16.basename(dir), rest) : path16.basename(dir);
|
|
17007
|
+
}
|
|
17008
|
+
}
|
|
17009
|
+
}, exempt = (cwd, root, file, plans) => {
|
|
17010
|
+
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
17011
|
+
const relative = path16.relative(fold(canonical2(root)), fold(canonical2(path16.resolve(cwd, file))));
|
|
17012
|
+
if (relative.startsWith("..") || path16.isAbsolute(relative))
|
|
17013
|
+
return true;
|
|
17014
|
+
const posix = relative.split(path16.sep).join("/");
|
|
17015
|
+
const segments = posix.split("/");
|
|
17016
|
+
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
16785
17017
|
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
|
|
16786
17018
|
let words = input;
|
|
16787
17019
|
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
@@ -16919,18 +17151,461 @@ var init_write_gate = __esm(() => {
|
|
|
16919
17151
|
cache2 = new Map;
|
|
16920
17152
|
});
|
|
16921
17153
|
|
|
17154
|
+
// packages/workit-core/src/hooks/raw-git.ts
|
|
17155
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
17156
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
17157
|
+
import fs12 from "node:fs";
|
|
17158
|
+
import os4 from "node:os";
|
|
17159
|
+
import path17 from "node:path";
|
|
17160
|
+
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
17161
|
+
const found = [];
|
|
17162
|
+
let dir = null;
|
|
17163
|
+
const groups = [];
|
|
17164
|
+
let stack = [];
|
|
17165
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
17166
|
+
if (segment.group === "open") {
|
|
17167
|
+
groups.push({ dir, stack: [...stack] });
|
|
17168
|
+
continue;
|
|
17169
|
+
}
|
|
17170
|
+
if (segment.group === "close") {
|
|
17171
|
+
const saved = groups.pop();
|
|
17172
|
+
if (saved)
|
|
17173
|
+
({ dir, stack } = saved);
|
|
17174
|
+
continue;
|
|
17175
|
+
}
|
|
17176
|
+
const words = stripPrefixes(segment.words);
|
|
17177
|
+
const name = commandName(words[0]);
|
|
17178
|
+
if (CD.has(name)) {
|
|
17179
|
+
const target = cdTarget(words);
|
|
17180
|
+
dir = target === null ? os4.homedir() : joinDir(dir, target);
|
|
17181
|
+
continue;
|
|
17182
|
+
}
|
|
17183
|
+
if (PUSHD.has(name)) {
|
|
17184
|
+
const target = cdTarget(words);
|
|
17185
|
+
stack.push(dir);
|
|
17186
|
+
dir = target === null || target === "-" ? undefined : joinDir(dir, target);
|
|
17187
|
+
continue;
|
|
17188
|
+
}
|
|
17189
|
+
if (POPD.has(name)) {
|
|
17190
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
17191
|
+
continue;
|
|
17192
|
+
}
|
|
17193
|
+
if (SHELLS.has(name) && depth < 3) {
|
|
17194
|
+
const flag = words.findIndex((word, index) => index > 0 && SCRIPT_FLAG.test(word));
|
|
17195
|
+
const script = flag > 0 ? words[flag + 1] : undefined;
|
|
17196
|
+
if (script)
|
|
17197
|
+
for (const inner of rawInvocations(script, "posix", depth + 1))
|
|
17198
|
+
found.push({
|
|
17199
|
+
...inner,
|
|
17200
|
+
dir: inner.dir === null ? dir : inner.dir === undefined ? undefined : joinDir(dir, inner.dir)
|
|
17201
|
+
});
|
|
17202
|
+
continue;
|
|
17203
|
+
}
|
|
17204
|
+
if (name === "gh" || name === "glab") {
|
|
17205
|
+
found.push({ tool: name, dir, args: words.slice(1) });
|
|
17206
|
+
continue;
|
|
17207
|
+
}
|
|
17208
|
+
if (name !== "git")
|
|
17209
|
+
continue;
|
|
17210
|
+
let here = dir;
|
|
17211
|
+
let index = 1;
|
|
17212
|
+
for (;index < words.length; index++) {
|
|
17213
|
+
const word = words[index];
|
|
17214
|
+
if (word === "-C")
|
|
17215
|
+
here = words[index + 1] ? joinDir(here, words[++index]) : here;
|
|
17216
|
+
else if (GIT_VALUE_OPTIONS.has(word))
|
|
17217
|
+
index++;
|
|
17218
|
+
else if (!word.startsWith("-"))
|
|
17219
|
+
break;
|
|
17220
|
+
}
|
|
17221
|
+
found.push({ tool: "git", dir: here, args: words.slice(index) });
|
|
17222
|
+
}
|
|
17223
|
+
return found;
|
|
17224
|
+
}
|
|
17225
|
+
function rawAction(invocation) {
|
|
17226
|
+
const args = invocation.args;
|
|
17227
|
+
if (asksHelp(invocation))
|
|
17228
|
+
return null;
|
|
17229
|
+
const [first, second] = args;
|
|
17230
|
+
if (invocation.tool === "git") {
|
|
17231
|
+
if (first === "commit")
|
|
17232
|
+
return args.includes("--dry-run") ? null : { kind: "commit", amend: args.includes("--amend") };
|
|
17233
|
+
if (first === "push")
|
|
17234
|
+
return pushAction(args);
|
|
17235
|
+
return null;
|
|
17236
|
+
}
|
|
17237
|
+
const forge = invocation.tool;
|
|
17238
|
+
const noun = forge === "gh" ? "pr" : "mr";
|
|
17239
|
+
if (first !== noun)
|
|
17240
|
+
return null;
|
|
17241
|
+
if (second === "merge")
|
|
17242
|
+
return { kind: "merge", forge };
|
|
17243
|
+
if (second === "create")
|
|
17244
|
+
return { kind: "pr-create", forge };
|
|
17245
|
+
if (["view", "checks", "status", "ci", "list"].includes(second ?? ""))
|
|
17246
|
+
return { kind: "pr-read", forge };
|
|
17247
|
+
return null;
|
|
17248
|
+
}
|
|
17249
|
+
function rawGitPre(input, command) {
|
|
17250
|
+
try {
|
|
17251
|
+
if (!mentionsRawTool(command))
|
|
17252
|
+
return NONE3;
|
|
17253
|
+
const nudges = [];
|
|
17254
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
|
|
17255
|
+
if (action.kind === "merge")
|
|
17256
|
+
return deny(refusal(action, null));
|
|
17257
|
+
if (action.kind === "push")
|
|
17258
|
+
for (const target of action.targets) {
|
|
17259
|
+
const branch = target ?? repo.branch;
|
|
17260
|
+
if (branch && guardedBranch(repo.dir, branch) && toPushRemote(repo, action.remote))
|
|
17261
|
+
return deny(refusal(action, branch));
|
|
17262
|
+
}
|
|
17263
|
+
const nudge = nudgeFor(action, input.host, input.session.id);
|
|
17264
|
+
if (nudge && !nudges.includes(nudge))
|
|
17265
|
+
nudges.push(nudge);
|
|
17266
|
+
}
|
|
17267
|
+
return nudges.length ? { kind: "context", text: nudges.join(`
|
|
17268
|
+
`) } : NONE3;
|
|
17269
|
+
} catch {
|
|
17270
|
+
return NONE3;
|
|
17271
|
+
}
|
|
17272
|
+
}
|
|
17273
|
+
function noteRawCommit(input, command, key) {
|
|
17274
|
+
try {
|
|
17275
|
+
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
17276
|
+
return;
|
|
17277
|
+
const at = Math.floor(Date.now() / 1000);
|
|
17278
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
|
|
17279
|
+
if (action.kind !== "commit")
|
|
17280
|
+
continue;
|
|
17281
|
+
const file = markerFile(repo, input.session.id, key);
|
|
17282
|
+
fs12.mkdirSync(path17.dirname(file), { recursive: true });
|
|
17283
|
+
fs12.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
17284
|
+
`);
|
|
17285
|
+
}
|
|
17286
|
+
} catch {}
|
|
17287
|
+
}
|
|
17288
|
+
function rawGitPost(input, command, key) {
|
|
17289
|
+
try {
|
|
17290
|
+
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
17291
|
+
return;
|
|
17292
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input)))
|
|
17293
|
+
if (action.kind === "commit")
|
|
17294
|
+
settle2(input, repo, markerFile(repo, input.session.id, key));
|
|
17295
|
+
} catch {}
|
|
17296
|
+
}
|
|
17297
|
+
function settlePendingCommit(input) {
|
|
17298
|
+
try {
|
|
17299
|
+
if (!input.session.id)
|
|
17300
|
+
return;
|
|
17301
|
+
const repo = repoAt(input.cwd);
|
|
17302
|
+
if (!repo)
|
|
17303
|
+
return;
|
|
17304
|
+
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
17305
|
+
if (fs12.existsSync(file))
|
|
17306
|
+
settle2(input, repo, file);
|
|
17307
|
+
} catch {}
|
|
17308
|
+
}
|
|
17309
|
+
var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, isAbsolute = (value) => path17.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), joinDir = (base, next) => {
|
|
17310
|
+
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
17311
|
+
return;
|
|
17312
|
+
const expanded = next === "~" || next.startsWith("~/") ? path17.join(os4.homedir(), next.slice(1)) : next;
|
|
17313
|
+
return base === null || isAbsolute(expanded) ? expanded : path17.join(base, expanded);
|
|
17314
|
+
}, cdTarget = (words) => {
|
|
17315
|
+
const args = words.slice(1);
|
|
17316
|
+
const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
|
|
17317
|
+
if (flag >= 0)
|
|
17318
|
+
return args[flag + 1] ?? null;
|
|
17319
|
+
return args.find((word) => word === "-" || !word.startsWith("-")) ?? null;
|
|
17320
|
+
}, stripPrefixes = (input) => {
|
|
17321
|
+
let words = input;
|
|
17322
|
+
for (;; ) {
|
|
17323
|
+
const first = words[0];
|
|
17324
|
+
if (first === undefined)
|
|
17325
|
+
return words;
|
|
17326
|
+
if (/^\w+=/.test(first))
|
|
17327
|
+
words = words.slice(1);
|
|
17328
|
+
else if (PREFIXES3.has(first)) {
|
|
17329
|
+
words = words.slice(1);
|
|
17330
|
+
while (words[0]?.startsWith("-"))
|
|
17331
|
+
words = words.slice(words[0] === "-u" ? 2 : 1);
|
|
17332
|
+
} else if (first === "timeout") {
|
|
17333
|
+
words = words.slice(1);
|
|
17334
|
+
while (words[0]?.startsWith("-"))
|
|
17335
|
+
words = words.slice(1);
|
|
17336
|
+
words = words.slice(1);
|
|
17337
|
+
} else
|
|
17338
|
+
return words;
|
|
17339
|
+
}
|
|
17340
|
+
}, commandName = (word) => (word?.split(/[\\/]/).at(-1) ?? "").toLowerCase().replace(/\.exe$/, ""), PUSH_VALUE_OPTIONS, pushAction = (args) => {
|
|
17341
|
+
let force = false;
|
|
17342
|
+
let lease = false;
|
|
17343
|
+
let wholesale = false;
|
|
17344
|
+
let remote = null;
|
|
17345
|
+
const positionals = [];
|
|
17346
|
+
for (let index = 1;index < args.length; index++) {
|
|
17347
|
+
const arg = args[index];
|
|
17348
|
+
if (arg === "--") {
|
|
17349
|
+
positionals.push(...args.slice(index + 1));
|
|
17350
|
+
break;
|
|
17351
|
+
}
|
|
17352
|
+
if (arg === "--dry-run" || /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg))
|
|
17353
|
+
return null;
|
|
17354
|
+
if (arg === "--repo")
|
|
17355
|
+
remote = args[++index] ?? null;
|
|
17356
|
+
else if (arg.startsWith("--repo="))
|
|
17357
|
+
remote = arg.slice("--repo=".length);
|
|
17358
|
+
else if (PUSH_VALUE_OPTIONS.has(arg))
|
|
17359
|
+
index++;
|
|
17360
|
+
else if (arg === "--force" || /^-[a-zA-Z]*f[a-zA-Z]*$/.test(arg))
|
|
17361
|
+
force = true;
|
|
17362
|
+
else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
|
|
17363
|
+
lease = true;
|
|
17364
|
+
else if (["--all", "--mirror", "--branches", "--tags"].includes(arg))
|
|
17365
|
+
wholesale = true;
|
|
17366
|
+
else if (!arg.startsWith("-"))
|
|
17367
|
+
positionals.push(arg);
|
|
17368
|
+
}
|
|
17369
|
+
const specs = remote === null ? positionals.slice(1) : positionals;
|
|
17370
|
+
remote ??= positionals[0] ?? null;
|
|
17371
|
+
const targets = [];
|
|
17372
|
+
for (const raw of specs) {
|
|
17373
|
+
if (raw.startsWith("+"))
|
|
17374
|
+
force = true;
|
|
17375
|
+
const spec = raw.replace(/^\+/, "");
|
|
17376
|
+
const colon = spec.indexOf(":");
|
|
17377
|
+
const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
|
|
17378
|
+
if (!destination)
|
|
17379
|
+
continue;
|
|
17380
|
+
if (destination === "HEAD")
|
|
17381
|
+
targets.push(null);
|
|
17382
|
+
else if (destination.startsWith("refs/heads/"))
|
|
17383
|
+
targets.push(destination.slice(11));
|
|
17384
|
+
else if (!destination.startsWith("refs/"))
|
|
17385
|
+
targets.push(destination);
|
|
17386
|
+
}
|
|
17387
|
+
if (specs.length === 0 && !wholesale)
|
|
17388
|
+
targets.push(null);
|
|
17389
|
+
return { kind: "push", targets, blindForce: force && !lease, remote };
|
|
17390
|
+
}, VALUE_OPTIONS, asksHelp = (invocation) => {
|
|
17391
|
+
const { tool, args } = invocation;
|
|
17392
|
+
if (args[0] === "help" || tool !== "git" && args[1] === "help")
|
|
17393
|
+
return true;
|
|
17394
|
+
const values = VALUE_OPTIONS[tool];
|
|
17395
|
+
for (let index = 0;index < args.length; index++) {
|
|
17396
|
+
const arg = args[index];
|
|
17397
|
+
if (arg === "--")
|
|
17398
|
+
return false;
|
|
17399
|
+
if (arg === "--help" || arg === "-h")
|
|
17400
|
+
return true;
|
|
17401
|
+
if (values.has(arg))
|
|
17402
|
+
index++;
|
|
17403
|
+
}
|
|
17404
|
+
return false;
|
|
17405
|
+
}, repoAt = (dir) => {
|
|
17406
|
+
const location = resolveStore(dir);
|
|
17407
|
+
if (location instanceof Error || !location.shared)
|
|
17408
|
+
return null;
|
|
17409
|
+
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
17410
|
+
}, isWorkitWorkspace = (repo) => {
|
|
17411
|
+
if (fs12.existsSync(repo.location.dir))
|
|
17412
|
+
return true;
|
|
17413
|
+
try {
|
|
17414
|
+
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
17415
|
+
} catch {
|
|
17416
|
+
return false;
|
|
17417
|
+
}
|
|
17418
|
+
}, guardedBranch = (dir, branch) => {
|
|
17419
|
+
try {
|
|
17420
|
+
const policy = resolveBranchPolicyFor(dir);
|
|
17421
|
+
return policy.protected.has(branch.toLowerCase()) || policy.defaultTargetBranch.toLowerCase() === branch.toLowerCase();
|
|
17422
|
+
} catch {
|
|
17423
|
+
return false;
|
|
17424
|
+
}
|
|
17425
|
+
}, toPushRemote = (repo, remote) => {
|
|
17426
|
+
if (remote === null)
|
|
17427
|
+
return true;
|
|
17428
|
+
try {
|
|
17429
|
+
const pushRemote = pushRemoteName(repo.dir, repo.branch);
|
|
17430
|
+
return pushRemote === null || pushRemote === remote;
|
|
17431
|
+
} catch {
|
|
17432
|
+
return false;
|
|
17433
|
+
}
|
|
17434
|
+
}, headSha2 = (repo) => {
|
|
17435
|
+
const git = repo.location.git;
|
|
17436
|
+
if (!git || git.reftable)
|
|
17437
|
+
return null;
|
|
17438
|
+
try {
|
|
17439
|
+
const head = fs12.readFileSync(path17.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
17440
|
+
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
17441
|
+
return head;
|
|
17442
|
+
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
17443
|
+
if (!ref)
|
|
17444
|
+
return null;
|
|
17445
|
+
const common = path17.dirname(repo.location.dir);
|
|
17446
|
+
try {
|
|
17447
|
+
return fs12.readFileSync(path17.join(common, ref), "utf8").trim() || null;
|
|
17448
|
+
} catch {}
|
|
17449
|
+
const packed = fs12.readFileSync(path17.join(common, "packed-refs"), "utf8");
|
|
17450
|
+
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
17451
|
+
} catch {
|
|
17452
|
+
return null;
|
|
17453
|
+
}
|
|
17454
|
+
}, quote = (text) => `\`${text}\``, refusal = (action, branch) => {
|
|
17455
|
+
if (action.kind === "merge")
|
|
17456
|
+
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")}).`;
|
|
17457
|
+
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")}.`;
|
|
17458
|
+
}, nudgeFor = (action, host, session) => {
|
|
17459
|
+
switch (action.kind) {
|
|
17460
|
+
case "commit": {
|
|
17461
|
+
const prefix = host === "cursor" && session ? `WORKIT_SESSION_ID=${session} ` : "";
|
|
17462
|
+
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.`;
|
|
17463
|
+
}
|
|
17464
|
+
case "push":
|
|
17465
|
+
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.`;
|
|
17466
|
+
case "pr-create":
|
|
17467
|
+
return `workit: prefer ${quote("workit pr create --fill")} (or --title/--body): it records the PR the merge gate checks.`;
|
|
17468
|
+
case "pr-read":
|
|
17469
|
+
return `workit: ${quote("workit pr status")} shows the PR, its checks and the verdict state; ${quote("workit ci wait")} waits for checks.`;
|
|
17470
|
+
case "merge":
|
|
17471
|
+
return null;
|
|
17472
|
+
}
|
|
17473
|
+
}, actionsIn = (cwd, command, dialect) => {
|
|
17474
|
+
const out = [];
|
|
17475
|
+
const repos = new Map;
|
|
17476
|
+
for (const invocation of rawInvocations(command, dialect)) {
|
|
17477
|
+
if (invocation.dir === undefined)
|
|
17478
|
+
continue;
|
|
17479
|
+
const action = rawAction(invocation);
|
|
17480
|
+
if (!action)
|
|
17481
|
+
continue;
|
|
17482
|
+
const dir = path17.resolve(cwd, invocation.dir ?? ".");
|
|
17483
|
+
if (!repos.has(dir)) {
|
|
17484
|
+
const repo = repoAt(dir);
|
|
17485
|
+
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
17486
|
+
}
|
|
17487
|
+
const repo = repos.get(dir);
|
|
17488
|
+
if (repo)
|
|
17489
|
+
out.push({ action, repo });
|
|
17490
|
+
}
|
|
17491
|
+
return out;
|
|
17492
|
+
}, mentionsRawTool = (command) => /\b(?:git|gh|glab)\b/i.test(command), dialectOf = (input) => (input.event.kind === "shell.pre" || input.event.kind === "shell.post" ? input.event.dialect : undefined) ?? "posix", deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path17.join(repo.location.dir, "hooks", `commit-${createHash10("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash10("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
|
|
17493
|
+
const run = spawnSync9("git", [
|
|
17494
|
+
"log",
|
|
17495
|
+
"-1",
|
|
17496
|
+
"--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
|
|
17497
|
+
], {
|
|
17498
|
+
cwd: dir,
|
|
17499
|
+
encoding: "utf8",
|
|
17500
|
+
timeout: 5000,
|
|
17501
|
+
windowsHide: true,
|
|
17502
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" }
|
|
17503
|
+
});
|
|
17504
|
+
const [sha, committed, subject = "", trailer = ""] = (run.status === 0 ? run.stdout.trim() : "").split("\x1F");
|
|
17505
|
+
return sha && /^[0-9a-f]{40,64}$/.test(sha) ? { sha, committed: Number(committed), subject, trailer: trailer.trim() || null } : null;
|
|
17506
|
+
}, settle2 = (input, repo, file) => {
|
|
17507
|
+
let marker;
|
|
17508
|
+
try {
|
|
17509
|
+
marker = JSON.parse(fs12.readFileSync(file, "utf8"));
|
|
17510
|
+
} catch {
|
|
17511
|
+
return;
|
|
17512
|
+
}
|
|
17513
|
+
fs12.rmSync(file, { force: true });
|
|
17514
|
+
const session = input.session.id;
|
|
17515
|
+
const now = Date.now() / 1000;
|
|
17516
|
+
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
17517
|
+
return;
|
|
17518
|
+
if (!session || !repo.branch)
|
|
17519
|
+
return;
|
|
17520
|
+
const head = headCommit(repo.dir);
|
|
17521
|
+
if (!head || head.sha === marker.head || head.committed < marker.at)
|
|
17522
|
+
return;
|
|
17523
|
+
if (head.trailer && head.trailer !== session)
|
|
17524
|
+
return;
|
|
17525
|
+
const ledger = readLedger(repo.dir);
|
|
17526
|
+
if (!ledger.ok)
|
|
17527
|
+
return;
|
|
17528
|
+
const claimed = ledger.value.rows.some((row) => row.type === "commit.recorded" && (row.sha === head.sha || row.head === head.sha));
|
|
17529
|
+
if (claimed)
|
|
17530
|
+
return;
|
|
17531
|
+
appendHookObserved(repo.dir, {
|
|
17532
|
+
type: "commit.recorded",
|
|
17533
|
+
actor: { host: input.host, session, agentId: input.session.agentId },
|
|
17534
|
+
branch: repo.branch,
|
|
17535
|
+
head: head.sha,
|
|
17536
|
+
sha: head.sha,
|
|
17537
|
+
session,
|
|
17538
|
+
agentId: input.session.agentId,
|
|
17539
|
+
subject: head.subject.slice(0, 200),
|
|
17540
|
+
files: [],
|
|
17541
|
+
fileCount: null,
|
|
17542
|
+
via: "raw_shell"
|
|
17543
|
+
});
|
|
17544
|
+
};
|
|
17545
|
+
var init_raw_git = __esm(() => {
|
|
17546
|
+
init_branch();
|
|
17547
|
+
init_workspaces();
|
|
17548
|
+
init_rev();
|
|
17549
|
+
init_ledger();
|
|
17550
|
+
init_paths();
|
|
17551
|
+
NONE3 = { kind: "none" };
|
|
17552
|
+
SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
17553
|
+
PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
17554
|
+
SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
|
|
17555
|
+
CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
17556
|
+
PUSHD = new Set(["pushd", "push-location"]);
|
|
17557
|
+
POPD = new Set(["popd", "pop-location"]);
|
|
17558
|
+
GIT_VALUE_OPTIONS = new Set([
|
|
17559
|
+
"-c",
|
|
17560
|
+
"--git-dir",
|
|
17561
|
+
"--work-tree",
|
|
17562
|
+
"--namespace",
|
|
17563
|
+
"--exec-path",
|
|
17564
|
+
"--config-env",
|
|
17565
|
+
"--super-prefix"
|
|
17566
|
+
]);
|
|
17567
|
+
PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
17568
|
+
VALUE_OPTIONS = {
|
|
17569
|
+
git: new Set([
|
|
17570
|
+
"-m",
|
|
17571
|
+
"--message",
|
|
17572
|
+
"-F",
|
|
17573
|
+
"--file",
|
|
17574
|
+
"-C",
|
|
17575
|
+
"-c",
|
|
17576
|
+
"--reuse-message",
|
|
17577
|
+
"--reedit-message",
|
|
17578
|
+
"--author",
|
|
17579
|
+
"--date",
|
|
17580
|
+
"--trailer",
|
|
17581
|
+
"-t",
|
|
17582
|
+
"--template",
|
|
17583
|
+
"--cleanup",
|
|
17584
|
+
"--fixup",
|
|
17585
|
+
"--squash",
|
|
17586
|
+
"-o",
|
|
17587
|
+
"--push-option",
|
|
17588
|
+
"--repo",
|
|
17589
|
+
"--receive-pack",
|
|
17590
|
+
"--exec"
|
|
17591
|
+
]),
|
|
17592
|
+
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"])),
|
|
17593
|
+
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"]))
|
|
17594
|
+
};
|
|
17595
|
+
});
|
|
17596
|
+
|
|
16922
17597
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
16923
17598
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
16924
|
-
import
|
|
17599
|
+
import path18 from "node:path";
|
|
16925
17600
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
|
|
16926
|
-
if (!nonEmpty2(value) || !
|
|
17601
|
+
if (!nonEmpty2(value) || !path18.isAbsolute(value) || !existsSync6(value))
|
|
16927
17602
|
return null;
|
|
16928
17603
|
try {
|
|
16929
17604
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
16930
17605
|
} catch {
|
|
16931
17606
|
return null;
|
|
16932
17607
|
}
|
|
16933
|
-
},
|
|
17608
|
+
}, SHELLS2, SCRIPT_FLAG2, quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg), commandText = (value) => {
|
|
16934
17609
|
if (nonEmpty2(value))
|
|
16935
17610
|
return value;
|
|
16936
17611
|
if (!Array.isArray(value) || value.length === 0)
|
|
@@ -16939,9 +17614,9 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
16939
17614
|
if (!argv.every((arg) => typeof arg === "string"))
|
|
16940
17615
|
return null;
|
|
16941
17616
|
const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
|
|
16942
|
-
if (
|
|
17617
|
+
if (SHELLS2.has(shell))
|
|
16943
17618
|
for (let index = 1;index < argv.length - 1; index++) {
|
|
16944
|
-
if (
|
|
17619
|
+
if (SCRIPT_FLAG2.test(argv[index]))
|
|
16945
17620
|
return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
|
|
16946
17621
|
if (!/^[-/]/.test(argv[index]))
|
|
16947
17622
|
break;
|
|
@@ -16959,8 +17634,8 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
16959
17634
|
return [...new Set(paths)];
|
|
16960
17635
|
};
|
|
16961
17636
|
var init_fields = __esm(() => {
|
|
16962
|
-
|
|
16963
|
-
|
|
17637
|
+
SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
17638
|
+
SCRIPT_FLAG2 = /^(?:-\w*c|-command|\/c)$/i;
|
|
16964
17639
|
WRITE_TOOLS = new Set([
|
|
16965
17640
|
"write",
|
|
16966
17641
|
"edit",
|
|
@@ -16990,7 +17665,7 @@ function handleHook(input, deps) {
|
|
|
16990
17665
|
switch (event.kind) {
|
|
16991
17666
|
case "session.start": {
|
|
16992
17667
|
if (!usable2(descriptor.context.sessionStart))
|
|
16993
|
-
return
|
|
17668
|
+
return NONE4;
|
|
16994
17669
|
const key = input.session.id ? `${input.host}\x00${input.session.id}` : null;
|
|
16995
17670
|
const offer = event.source === "startup" && (key === null || !offered.has(key));
|
|
16996
17671
|
if (offer && key !== null)
|
|
@@ -17005,19 +17680,34 @@ function handleHook(input, deps) {
|
|
|
17005
17680
|
}
|
|
17006
17681
|
case "context.turn": {
|
|
17007
17682
|
if (!usable2(descriptor.context.perTurn))
|
|
17008
|
-
return
|
|
17683
|
+
return NONE4;
|
|
17009
17684
|
const text = turnContextText(input, descriptor);
|
|
17010
|
-
return text ? { kind: "context", text } :
|
|
17685
|
+
return text ? { kind: "context", text } : NONE4;
|
|
17011
17686
|
}
|
|
17012
17687
|
case "shell.pre": {
|
|
17013
|
-
const
|
|
17014
|
-
|
|
17688
|
+
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
17689
|
+
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
|
|
17690
|
+
if (policy.kind !== "none")
|
|
17015
17691
|
return policy;
|
|
17016
|
-
const
|
|
17017
|
-
|
|
17692
|
+
const postTool = usable2(descriptor.events["shell.post"].support);
|
|
17693
|
+
if (!postTool)
|
|
17694
|
+
settlePendingCommit(input);
|
|
17695
|
+
const raw = canDeny ? rawGitPre(input, event.command) : NONE4;
|
|
17696
|
+
if (raw.kind === "deny")
|
|
17697
|
+
return raw;
|
|
17698
|
+
const writes = usable2(descriptor.events["write.pre"].support) ? shellWrites(event.command) : { writes: false, targets: null };
|
|
17699
|
+
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE4;
|
|
17700
|
+
if (gate.kind === "deny")
|
|
17701
|
+
return gate;
|
|
17702
|
+
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
17703
|
+
return raw;
|
|
17018
17704
|
}
|
|
17705
|
+
case "shell.post":
|
|
17706
|
+
if (usable2(descriptor.events["shell.post"].support))
|
|
17707
|
+
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
17708
|
+
return NONE4;
|
|
17019
17709
|
case "write.pre":
|
|
17020
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) :
|
|
17710
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE4;
|
|
17021
17711
|
case "subagent.start":
|
|
17022
17712
|
return {
|
|
17023
17713
|
kind: "context",
|
|
@@ -17029,14 +17719,13 @@ function handleHook(input, deps) {
|
|
|
17029
17719
|
userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
|
|
17030
17720
|
};
|
|
17031
17721
|
case "tool.pre":
|
|
17032
|
-
case "shell.post":
|
|
17033
17722
|
case "subagent.stop":
|
|
17034
17723
|
case "prompt.submit":
|
|
17035
17724
|
case "stop":
|
|
17036
|
-
return
|
|
17725
|
+
return NONE4;
|
|
17037
17726
|
}
|
|
17038
17727
|
}
|
|
17039
|
-
var usable2 = (value) => value === "native" || value === "partial",
|
|
17728
|
+
var usable2 = (value) => value === "native" || value === "partial", NONE4, offered, CLAUDE_WORKTREE_IMPLEMENTER = "workit:implementer", CLAUDE_JUDGES, subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128), subagentStartText = (host, session, descriptor, event) => {
|
|
17040
17729
|
if (host === "claude_code" && event.agentType === CLAUDE_WORKTREE_IMPLEMENTER)
|
|
17041
17730
|
return `Workit observed ${descriptor.label} subagent ${event.agentId} (${event.agentType}) working in its own git worktree: it may edit and commit there, within its brief's scope. Before the first commit, switch to a policy-compliant branch with \`workit git branch <branch> --base <base>\` (e.g. feature/<slug>); branch policy hooks still deny protected or non-compliant branches. Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
17042
17731
|
const readOnly = `Workit observed ${descriptor.label} subagent ${event.agentId} (${event.agentType}) as read-only/agent-guided.`;
|
|
@@ -17045,10 +17734,10 @@ var usable2 = (value) => value === "native" || value === "partial", NONE3, offer
|
|
|
17045
17734
|
return readOnly;
|
|
17046
17735
|
}, failureDecision = (descriptor, event, error) => {
|
|
17047
17736
|
if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
|
|
17048
|
-
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } :
|
|
17737
|
+
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE4;
|
|
17049
17738
|
if (event === "session.start" || event === "subagent.start")
|
|
17050
17739
|
return { kind: "context", text: `[workit diagnostic: ${error}]` };
|
|
17051
|
-
return
|
|
17740
|
+
return NONE4;
|
|
17052
17741
|
}, dispatchHook = (adapter, raw, env = process.env) => {
|
|
17053
17742
|
const parsed = adapter.parse(raw, env);
|
|
17054
17743
|
if (!parsed.ok)
|
|
@@ -17074,14 +17763,15 @@ var usable2 = (value) => value === "native" || value === "partial", NONE3, offer
|
|
|
17074
17763
|
var init_handle = __esm(() => {
|
|
17075
17764
|
init_context();
|
|
17076
17765
|
init_policy();
|
|
17766
|
+
init_raw_git();
|
|
17077
17767
|
init_write_gate();
|
|
17078
|
-
|
|
17768
|
+
NONE4 = { kind: "none" };
|
|
17079
17769
|
offered = new Set;
|
|
17080
17770
|
CLAUDE_JUDGES = new Set(["workit:verifier", "workit:reviewer"]);
|
|
17081
17771
|
});
|
|
17082
17772
|
|
|
17083
17773
|
// packages/workit-core/src/hooks/hosts/claude-code.ts
|
|
17084
|
-
var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, toolCommand = (value) => isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null, eventOf = (name, value) => {
|
|
17774
|
+
var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix", toolCommand = (value) => isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null, eventOf = (name, value) => {
|
|
17085
17775
|
const toolUseId = optionalText(value.tool_use_id);
|
|
17086
17776
|
switch (name) {
|
|
17087
17777
|
case "SessionStart":
|
|
@@ -17104,7 +17794,7 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, toolCommand = (value)
|
|
|
17104
17794
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
17105
17795
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
17106
17796
|
const command = toolCommand(value);
|
|
17107
|
-
return command ? { ok: true, event: { kind: "shell.pre", command, toolUseId } } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
|
|
17797
|
+
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}` };
|
|
17108
17798
|
}
|
|
17109
17799
|
case "PostToolUse": {
|
|
17110
17800
|
const command = toolCommand(value);
|
|
@@ -17118,7 +17808,8 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, toolCommand = (value)
|
|
|
17118
17808
|
command,
|
|
17119
17809
|
stdout: optionalText(response.stdout) ?? "",
|
|
17120
17810
|
exitCode: typeof response.exit_code === "number" ? response.exit_code : null,
|
|
17121
|
-
toolUseId
|
|
17811
|
+
toolUseId,
|
|
17812
|
+
dialect: dialectOf2(value)
|
|
17122
17813
|
}
|
|
17123
17814
|
};
|
|
17124
17815
|
}
|
|
@@ -17281,7 +17972,13 @@ var init_claude_code = __esm(() => {
|
|
|
17281
17972
|
};
|
|
17282
17973
|
SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
17283
17974
|
SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
|
|
17284
|
-
CONTEXT_EVENTS = new Set([
|
|
17975
|
+
CONTEXT_EVENTS = new Set([
|
|
17976
|
+
"SessionStart",
|
|
17977
|
+
"UserPromptSubmit",
|
|
17978
|
+
"SubagentStart",
|
|
17979
|
+
"PreToolUse",
|
|
17980
|
+
"PostToolUse"
|
|
17981
|
+
]);
|
|
17285
17982
|
claudeCodeAdapter = {
|
|
17286
17983
|
descriptor: CLAUDE_CODE_DESCRIPTOR,
|
|
17287
17984
|
parse(raw) {
|
|
@@ -17335,10 +18032,11 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
17335
18032
|
const cwd = existingDirectory(value.cwd);
|
|
17336
18033
|
if (!cwd)
|
|
17337
18034
|
return { ok: false, error: "cwd must be an existing absolute directory" };
|
|
17338
|
-
|
|
18035
|
+
const toolEvent = event === "PreToolUse" || event === "PostToolUse";
|
|
18036
|
+
if (toolEvent && !nonEmpty2(value.tool_name))
|
|
17339
18037
|
return { ok: false, error: "tool_name is required" };
|
|
17340
18038
|
const command = isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
|
|
17341
|
-
if (
|
|
18039
|
+
if (toolEvent && isShellTool(value.tool_name) && !command)
|
|
17342
18040
|
return { ok: false, error: "tool_input.command is required for shell tools" };
|
|
17343
18041
|
if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
|
|
17344
18042
|
return { ok: false, error: "agent_id is required" };
|
|
@@ -17354,7 +18052,8 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
17354
18052
|
...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
|
|
17355
18053
|
...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
|
|
17356
18054
|
...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
|
|
17357
|
-
...
|
|
18055
|
+
...toolEvent ? { tool_input: value.tool_input } : {},
|
|
18056
|
+
...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
|
|
17358
18057
|
...command ? { command } : {},
|
|
17359
18058
|
...nonEmpty2(value.tool_use_id) ? { tool_use_id: value.tool_use_id } : {},
|
|
17360
18059
|
...nonEmpty2(value.agent_id) ? { agent_id: value.agent_id } : {},
|
|
@@ -17378,6 +18077,16 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
17378
18077
|
toolUseId
|
|
17379
18078
|
} : { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
17380
18079
|
}
|
|
18080
|
+
case "PostToolUse": {
|
|
18081
|
+
const toolUseId = input.tool_use_id ?? null;
|
|
18082
|
+
return isShellTool(input.tool_name) ? {
|
|
18083
|
+
kind: "shell.post",
|
|
18084
|
+
command: input.command ?? "",
|
|
18085
|
+
stdout: input.tool_response ?? "",
|
|
18086
|
+
exitCode: null,
|
|
18087
|
+
toolUseId
|
|
18088
|
+
} : { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
18089
|
+
}
|
|
17381
18090
|
case "SubagentStart":
|
|
17382
18091
|
return {
|
|
17383
18092
|
kind: "subagent.start",
|
|
@@ -17426,7 +18135,7 @@ var init_codex = __esm(() => {
|
|
|
17426
18135
|
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
17427
18136
|
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
17428
18137
|
"write.pre": { support: "undocumented", native: null },
|
|
17429
|
-
"shell.post":
|
|
18138
|
+
"shell.post": { support: "native", native: "PostToolUse" },
|
|
17430
18139
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
17431
18140
|
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
17432
18141
|
"prompt.submit": undocumented,
|
|
@@ -17447,7 +18156,7 @@ var init_codex = __esm(() => {
|
|
|
17447
18156
|
worktreeIsolation: "undocumented",
|
|
17448
18157
|
maxConcurrency: "undocumented"
|
|
17449
18158
|
},
|
|
17450
|
-
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "
|
|
18159
|
+
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "native" },
|
|
17451
18160
|
interaction: { questions: "none", writeBoundary: "partial" },
|
|
17452
18161
|
stopControl: "undocumented",
|
|
17453
18162
|
shellAvailable: "native",
|
|
@@ -17513,6 +18222,7 @@ var init_codex = __esm(() => {
|
|
|
17513
18222
|
EVENTS2 = {
|
|
17514
18223
|
SessionStart: "session.start",
|
|
17515
18224
|
PreToolUse: "shell.pre",
|
|
18225
|
+
PostToolUse: "shell.post",
|
|
17516
18226
|
SubagentStart: "subagent.start",
|
|
17517
18227
|
SubagentStop: "subagent.stop"
|
|
17518
18228
|
};
|
|
@@ -17547,12 +18257,12 @@ var init_codex = __esm(() => {
|
|
|
17547
18257
|
};
|
|
17548
18258
|
},
|
|
17549
18259
|
render: render2,
|
|
17550
|
-
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>`
|
|
18260
|
+
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>`
|
|
17551
18261
|
};
|
|
17552
18262
|
});
|
|
17553
18263
|
|
|
17554
18264
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
17555
|
-
import
|
|
18265
|
+
import path19 from "node:path";
|
|
17556
18266
|
var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
17557
18267
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
17558
18268
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -17621,7 +18331,7 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
17621
18331
|
case "preCompact":
|
|
17622
18332
|
return { kind: "compact.pre", trigger: "auto" };
|
|
17623
18333
|
}
|
|
17624
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
18334
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path19.sep) ? root : `${root}${path19.sep}`), hookCwd = (input) => {
|
|
17625
18335
|
const cwd = existingDirectory(input.cwd);
|
|
17626
18336
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
17627
18337
|
return cwd;
|
|
@@ -17639,6 +18349,8 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
17639
18349
|
json: cursorDeny(decision.reason),
|
|
17640
18350
|
exitCode: native !== null && BLOCKING.has(native) ? 2 : 0
|
|
17641
18351
|
};
|
|
18352
|
+
if (decision.kind === "context" && native === "beforeShellExecution")
|
|
18353
|
+
return { json: { permission: "allow", agent_message: decision.text }, exitCode: 0 };
|
|
17642
18354
|
if (decision.kind === "context")
|
|
17643
18355
|
return { json: { additional_context: decision.text }, exitCode: 0 };
|
|
17644
18356
|
if (decision.kind === "notice")
|
|
@@ -17788,7 +18500,8 @@ var init_cursor = __esm(() => {
|
|
|
17788
18500
|
}
|
|
17789
18501
|
};
|
|
17790
18502
|
},
|
|
17791
|
-
render: render3
|
|
18503
|
+
render: render3,
|
|
18504
|
+
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
|
|
17792
18505
|
};
|
|
17793
18506
|
});
|
|
17794
18507
|
|
|
@@ -17838,6 +18551,7 @@ var init_hooks = __esm(async () => {
|
|
|
17838
18551
|
init_context();
|
|
17839
18552
|
init_policy();
|
|
17840
18553
|
init_write_gate();
|
|
18554
|
+
init_raw_git();
|
|
17841
18555
|
init_fields();
|
|
17842
18556
|
init_handle();
|
|
17843
18557
|
init_claude_code();
|