@brainervirus/workit-cursor 8.13.1 → 8.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp-server.js
CHANGED
|
@@ -21983,6 +21983,8 @@ var workspaceConfigSchema = object2({
|
|
|
21983
21983
|
defaultEndpoint: unknown().optional(),
|
|
21984
21984
|
verification: _enum(["self", "independent"]).optional(),
|
|
21985
21985
|
trailers: boolean2().optional(),
|
|
21986
|
+
liveSessionGuard: boolean2().optional(),
|
|
21987
|
+
fanout: object2({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
21986
21988
|
autonomy: object2(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
21987
21989
|
kind,
|
|
21988
21990
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -22300,6 +22302,17 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
22300
22302
|
const matched = matchRuntimeWorkspace(cwd);
|
|
22301
22303
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
22302
22304
|
};
|
|
22305
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
22306
|
+
var TIER_HOSTS = new Set([
|
|
22307
|
+
"claude_code",
|
|
22308
|
+
"opencode",
|
|
22309
|
+
"codex",
|
|
22310
|
+
"codex_cli",
|
|
22311
|
+
"codex_desktop",
|
|
22312
|
+
"cursor",
|
|
22313
|
+
"pi",
|
|
22314
|
+
"default"
|
|
22315
|
+
]);
|
|
22303
22316
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
22304
22317
|
const matched = matchRuntimeWorkspace(cwd);
|
|
22305
22318
|
if (!matched)
|
|
@@ -28462,10 +28475,44 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
28462
28475
|
var signals = new Map;
|
|
28463
28476
|
var cache = new Map;
|
|
28464
28477
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
28478
|
+
// packages/workit-core/src/core/live-session.ts
|
|
28479
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
28480
|
+
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
28481
|
+
var liveProbe = {
|
|
28482
|
+
platform: process.platform,
|
|
28483
|
+
run: (args) => spawnSync15("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
28484
|
+
start: (pid) => processStartOf(pid)
|
|
28485
|
+
};
|
|
28486
|
+
var PIDS = new Map;
|
|
28487
|
+
|
|
28465
28488
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
28466
28489
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
28467
28490
|
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
28468
28491
|
var cache2 = new Map;
|
|
28492
|
+
var MUTATING_GIT = new Set([
|
|
28493
|
+
"add",
|
|
28494
|
+
"am",
|
|
28495
|
+
"apply",
|
|
28496
|
+
"checkout",
|
|
28497
|
+
"cherry-pick",
|
|
28498
|
+
"clean",
|
|
28499
|
+
"commit",
|
|
28500
|
+
"merge",
|
|
28501
|
+
"mv",
|
|
28502
|
+
"pull",
|
|
28503
|
+
"rebase",
|
|
28504
|
+
"reset",
|
|
28505
|
+
"restore",
|
|
28506
|
+
"revert",
|
|
28507
|
+
"rm",
|
|
28508
|
+
"stash",
|
|
28509
|
+
"switch"
|
|
28510
|
+
]);
|
|
28511
|
+
var DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
28512
|
+
var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
28513
|
+
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
28514
|
+
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
28515
|
+
var POPD2 = new Set(["popd", "pop-location"]);
|
|
28469
28516
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
28470
28517
|
if (false) {}
|
|
28471
28518
|
|
package/dist/workit-hook.js
CHANGED
|
@@ -9355,6 +9355,8 @@ var workspaceConfigSchema = object({
|
|
|
9355
9355
|
defaultEndpoint: unknown().optional(),
|
|
9356
9356
|
verification: _enum(["self", "independent"]).optional(),
|
|
9357
9357
|
trailers: boolean2().optional(),
|
|
9358
|
+
liveSessionGuard: boolean2().optional(),
|
|
9359
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
9358
9360
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
9359
9361
|
kind,
|
|
9360
9362
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -9672,24 +9674,39 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
9672
9674
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9673
9675
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
9674
9676
|
};
|
|
9675
|
-
var
|
|
9677
|
+
var inheritedSetting = (cwd, dir, has) => {
|
|
9676
9678
|
let matched;
|
|
9677
9679
|
try {
|
|
9678
9680
|
matched = matchRuntimeWorkspace(cwd, dir);
|
|
9679
9681
|
} catch {
|
|
9680
|
-
return
|
|
9682
|
+
return null;
|
|
9681
9683
|
}
|
|
9682
9684
|
if (!matched)
|
|
9683
|
-
return
|
|
9685
|
+
return null;
|
|
9684
9686
|
const { selected, matches } = matched;
|
|
9685
9687
|
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
9686
|
-
|
|
9688
|
+
return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
|
|
9689
|
+
};
|
|
9690
|
+
var workspaceTrailers = (cwd, dir) => {
|
|
9691
|
+
const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
|
|
9687
9692
|
return {
|
|
9688
|
-
enabled: source?.trailers !== false,
|
|
9689
|
-
workspace:
|
|
9690
|
-
from: source?.name ?? null
|
|
9693
|
+
enabled: found?.source?.trailers !== false,
|
|
9694
|
+
workspace: found?.workspace ?? null,
|
|
9695
|
+
from: found?.source?.name ?? null
|
|
9691
9696
|
};
|
|
9692
9697
|
};
|
|
9698
|
+
var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
|
|
9699
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
9700
|
+
var TIER_HOSTS = new Set([
|
|
9701
|
+
"claude_code",
|
|
9702
|
+
"opencode",
|
|
9703
|
+
"codex",
|
|
9704
|
+
"codex_cli",
|
|
9705
|
+
"codex_desktop",
|
|
9706
|
+
"cursor",
|
|
9707
|
+
"pi",
|
|
9708
|
+
"default"
|
|
9709
|
+
]);
|
|
9693
9710
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
9694
9711
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9695
9712
|
if (!matched)
|
|
@@ -20220,27 +20237,231 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
20220
20237
|
};
|
|
20221
20238
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
20222
20239
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
20240
|
+
import os7 from "node:os";
|
|
20241
|
+
import path23 from "node:path";
|
|
20242
|
+
|
|
20243
|
+
// packages/workit-core/src/core/live-session.ts
|
|
20244
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20245
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
20246
|
+
import fs17 from "node:fs";
|
|
20247
|
+
import os6 from "node:os";
|
|
20223
20248
|
import path22 from "node:path";
|
|
20249
|
+
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
20250
|
+
var HEARTBEAT_EVERY_MS = 30000;
|
|
20251
|
+
var liveProbe = {
|
|
20252
|
+
platform: process.platform,
|
|
20253
|
+
run: (args) => spawnSync13("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
20254
|
+
start: (pid) => processStartOf(pid)
|
|
20255
|
+
};
|
|
20256
|
+
var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
|
|
20257
|
+
var familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, "");
|
|
20258
|
+
var processAlive = (pid) => {
|
|
20259
|
+
try {
|
|
20260
|
+
process.kill(pid, 0);
|
|
20261
|
+
return true;
|
|
20262
|
+
} catch (error) {
|
|
20263
|
+
return error.code === "EPERM";
|
|
20264
|
+
}
|
|
20265
|
+
};
|
|
20266
|
+
var processInfo = (pid) => {
|
|
20267
|
+
try {
|
|
20268
|
+
if (liveProbe.platform === "linux") {
|
|
20269
|
+
const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
20270
|
+
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
20271
|
+
const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
20272
|
+
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
20273
|
+
}
|
|
20274
|
+
if (liveProbe.platform === "win32")
|
|
20275
|
+
return null;
|
|
20276
|
+
const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
|
|
20277
|
+
return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
|
|
20278
|
+
} catch {
|
|
20279
|
+
return null;
|
|
20280
|
+
}
|
|
20281
|
+
};
|
|
20282
|
+
var WRAPPER = /^(?:\S*\/)?(?:sh|bash|dash|zsh|ash|ksh|fish|env|timeout|npx|npm|bunx|cmd\.exe|pwsh|powershell)(?:\s|$)|launch\.mjs|npx-cli\.js/u;
|
|
20283
|
+
var PIDS = new Map;
|
|
20284
|
+
var hostPid = (transport) => {
|
|
20285
|
+
if (transport !== "hook-process")
|
|
20286
|
+
return process.pid;
|
|
20287
|
+
const key = `${liveProbe.platform}\x00${process.ppid}`;
|
|
20288
|
+
if (PIDS.has(key))
|
|
20289
|
+
return PIDS.get(key) ?? null;
|
|
20290
|
+
if (liveProbe.platform === "win32") {
|
|
20291
|
+
PIDS.set(key, null);
|
|
20292
|
+
return null;
|
|
20293
|
+
}
|
|
20294
|
+
let pid = process.ppid;
|
|
20295
|
+
for (let hop = 0;hop < 4; hop++) {
|
|
20296
|
+
const info = processInfo(pid);
|
|
20297
|
+
if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
|
|
20298
|
+
break;
|
|
20299
|
+
pid = info.ppid;
|
|
20300
|
+
}
|
|
20301
|
+
PIDS.set(key, pid);
|
|
20302
|
+
return pid;
|
|
20303
|
+
};
|
|
20304
|
+
var sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-");
|
|
20305
|
+
var liveOwner = (input, transport, env = process.env) => {
|
|
20306
|
+
const session = input.session.id?.trim();
|
|
20307
|
+
if (!session)
|
|
20308
|
+
return null;
|
|
20309
|
+
const leads = new Set([leadOf(sessionSafe(session))]);
|
|
20310
|
+
if (input.session.parentId)
|
|
20311
|
+
leads.add(leadOf(sessionSafe(input.session.parentId)));
|
|
20312
|
+
if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
|
|
20313
|
+
leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
|
|
20314
|
+
return {
|
|
20315
|
+
host: input.host,
|
|
20316
|
+
session: sessionSafe(session),
|
|
20317
|
+
leads: [...leads],
|
|
20318
|
+
pid: () => hostPid(transport)
|
|
20319
|
+
};
|
|
20320
|
+
};
|
|
20321
|
+
var checkoutOf = (target) => {
|
|
20322
|
+
let dir = path22.resolve(target);
|
|
20323
|
+
while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
|
|
20324
|
+
dir = path22.dirname(dir);
|
|
20325
|
+
if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
|
|
20326
|
+
dir = path22.dirname(dir);
|
|
20327
|
+
const parts = dir.split(path22.sep);
|
|
20328
|
+
const dotGit = parts.lastIndexOf(".git");
|
|
20329
|
+
if (dotGit > 0)
|
|
20330
|
+
dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
|
|
20331
|
+
const location = resolveStore(dir);
|
|
20332
|
+
if (location instanceof Error || !location.shared || !location.top)
|
|
20333
|
+
return null;
|
|
20334
|
+
let top = location.top;
|
|
20335
|
+
try {
|
|
20336
|
+
top = fs17.realpathSync.native(top);
|
|
20337
|
+
} catch {}
|
|
20338
|
+
return top;
|
|
20339
|
+
};
|
|
20340
|
+
var markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
20341
|
+
var checkoutKey = (top, platform = process.platform) => {
|
|
20342
|
+
let key = path22.resolve(top);
|
|
20343
|
+
try {
|
|
20344
|
+
key = fs17.realpathSync.native(key);
|
|
20345
|
+
} catch {}
|
|
20346
|
+
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
20347
|
+
};
|
|
20348
|
+
var readMarker = (file) => {
|
|
20349
|
+
try {
|
|
20350
|
+
const value = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
20351
|
+
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
20352
|
+
return null;
|
|
20353
|
+
return {
|
|
20354
|
+
version: 1,
|
|
20355
|
+
session: value.session,
|
|
20356
|
+
leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
|
|
20357
|
+
host: value.host,
|
|
20358
|
+
pid: typeof value.pid === "number" ? value.pid : null,
|
|
20359
|
+
pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
|
|
20360
|
+
machine: typeof value.machine === "string" ? value.machine : "",
|
|
20361
|
+
checkout: typeof value.checkout === "string" ? value.checkout : "",
|
|
20362
|
+
heartbeatAt: value.heartbeatAt
|
|
20363
|
+
};
|
|
20364
|
+
} catch {
|
|
20365
|
+
return null;
|
|
20366
|
+
}
|
|
20367
|
+
};
|
|
20368
|
+
var ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt);
|
|
20369
|
+
var isLive = (marker, deps) => {
|
|
20370
|
+
const age = ageOf2(marker, deps.now ?? new Date);
|
|
20371
|
+
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
20372
|
+
return false;
|
|
20373
|
+
if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
|
|
20374
|
+
return true;
|
|
20375
|
+
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
20376
|
+
return false;
|
|
20377
|
+
if (marker.pidStart === null)
|
|
20378
|
+
return true;
|
|
20379
|
+
const start = liveProbe.start(marker.pid);
|
|
20380
|
+
return start === null || start === marker.pidStart;
|
|
20381
|
+
};
|
|
20382
|
+
var sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead));
|
|
20383
|
+
var sameOwner = (marker, owner, machine) => {
|
|
20384
|
+
if (sameLead(marker, owner))
|
|
20385
|
+
return true;
|
|
20386
|
+
if (marker.machine !== machine)
|
|
20387
|
+
return false;
|
|
20388
|
+
const pid = owner.pid();
|
|
20389
|
+
if (pid === null || marker.pid === null || marker.pidStart === null)
|
|
20390
|
+
return familyOf(marker.host) === familyOf(owner.host);
|
|
20391
|
+
return marker.pid === pid;
|
|
20392
|
+
};
|
|
20393
|
+
var liveConflict = (target, owner, deps = {}) => {
|
|
20394
|
+
try {
|
|
20395
|
+
const checkout = checkoutOf(target);
|
|
20396
|
+
if (!checkout)
|
|
20397
|
+
return null;
|
|
20398
|
+
const file = markerFile2(checkout);
|
|
20399
|
+
const marker = readMarker(file);
|
|
20400
|
+
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
20401
|
+
return null;
|
|
20402
|
+
if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
|
|
20403
|
+
return null;
|
|
20404
|
+
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
20405
|
+
} catch {
|
|
20406
|
+
return null;
|
|
20407
|
+
}
|
|
20408
|
+
};
|
|
20409
|
+
var claimLiveSession = (target, owner, deps = {}) => {
|
|
20410
|
+
try {
|
|
20411
|
+
const checkout = checkoutOf(target);
|
|
20412
|
+
if (!checkout)
|
|
20413
|
+
return;
|
|
20414
|
+
const file = markerFile2(checkout);
|
|
20415
|
+
const now = deps.now ?? new Date;
|
|
20416
|
+
const machine = deps.machine ?? os6.hostname();
|
|
20417
|
+
const current = readMarker(file);
|
|
20418
|
+
if (current) {
|
|
20419
|
+
const age = ageOf2(current, now);
|
|
20420
|
+
if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
|
|
20421
|
+
return;
|
|
20422
|
+
if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
|
|
20423
|
+
return;
|
|
20424
|
+
}
|
|
20425
|
+
const pid = owner.pid();
|
|
20426
|
+
const marker = {
|
|
20427
|
+
version: 1,
|
|
20428
|
+
session: owner.session,
|
|
20429
|
+
leads: owner.leads,
|
|
20430
|
+
host: owner.host,
|
|
20431
|
+
pid,
|
|
20432
|
+
pidStart: pid === null ? null : liveProbe.start(pid),
|
|
20433
|
+
machine,
|
|
20434
|
+
checkout,
|
|
20435
|
+
heartbeatAt: now.toISOString()
|
|
20436
|
+
};
|
|
20437
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
20438
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
20439
|
+
fs17.writeFileSync(temp, JSON.stringify(marker));
|
|
20440
|
+
fs17.renameSync(temp, file);
|
|
20441
|
+
} catch {}
|
|
20442
|
+
};
|
|
20443
|
+
|
|
20444
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
20224
20445
|
var NONE3 = { kind: "none" };
|
|
20225
20446
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20226
20447
|
var canonical2 = (file) => {
|
|
20227
20448
|
let rest = "";
|
|
20228
|
-
for (let dir =
|
|
20449
|
+
for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
|
|
20229
20450
|
try {
|
|
20230
|
-
return
|
|
20451
|
+
return path23.join(realpathSync5(dir), rest);
|
|
20231
20452
|
} catch {
|
|
20232
|
-
if (
|
|
20233
|
-
return
|
|
20234
|
-
rest = rest ?
|
|
20453
|
+
if (path23.dirname(dir) === dir)
|
|
20454
|
+
return path23.resolve(file);
|
|
20455
|
+
rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
|
|
20235
20456
|
}
|
|
20236
20457
|
}
|
|
20237
20458
|
};
|
|
20238
20459
|
var exempt = (cwd, root, file, plans) => {
|
|
20239
20460
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20240
|
-
const relative =
|
|
20241
|
-
if (relative.startsWith("..") ||
|
|
20461
|
+
const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
|
|
20462
|
+
if (relative.startsWith("..") || path23.isAbsolute(relative))
|
|
20242
20463
|
return true;
|
|
20243
|
-
const posix = relative.split(
|
|
20464
|
+
const posix = relative.split(path23.sep).join("/");
|
|
20244
20465
|
const segments = posix.split("/");
|
|
20245
20466
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20246
20467
|
};
|
|
@@ -20338,7 +20559,7 @@ var shellWrites = (command) => {
|
|
|
20338
20559
|
return { writes: true, targets: unknown ? null : targets };
|
|
20339
20560
|
};
|
|
20340
20561
|
var cache2 = new Map;
|
|
20341
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20562
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00");
|
|
20342
20563
|
var gateFor = (cwd) => {
|
|
20343
20564
|
const store = new TaskStore(cwd);
|
|
20344
20565
|
const listed = store.listTaskIndex();
|
|
@@ -20376,7 +20597,218 @@ var gateFor = (cwd) => {
|
|
|
20376
20597
|
cache2.delete(cache2.keys().next().value);
|
|
20377
20598
|
return gate;
|
|
20378
20599
|
};
|
|
20379
|
-
|
|
20600
|
+
var MUTATING_GIT = new Set([
|
|
20601
|
+
"add",
|
|
20602
|
+
"am",
|
|
20603
|
+
"apply",
|
|
20604
|
+
"checkout",
|
|
20605
|
+
"cherry-pick",
|
|
20606
|
+
"clean",
|
|
20607
|
+
"commit",
|
|
20608
|
+
"merge",
|
|
20609
|
+
"mv",
|
|
20610
|
+
"pull",
|
|
20611
|
+
"rebase",
|
|
20612
|
+
"reset",
|
|
20613
|
+
"restore",
|
|
20614
|
+
"revert",
|
|
20615
|
+
"rm",
|
|
20616
|
+
"stash",
|
|
20617
|
+
"switch"
|
|
20618
|
+
]);
|
|
20619
|
+
var DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
20620
|
+
var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
20621
|
+
var DIR_TARGET = ".workit-live-target";
|
|
20622
|
+
var OVERRIDE = "WORKIT_LIVE_SESSION=override";
|
|
20623
|
+
var inert = (args, sub) => args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run" || DRY_N.has(sub) && /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg) && !arg.startsWith("--"));
|
|
20624
|
+
var gitMutationDirs = (cwd, command, dialect) => {
|
|
20625
|
+
let invocations;
|
|
20626
|
+
try {
|
|
20627
|
+
invocations = rawInvocations(command, dialect);
|
|
20628
|
+
} catch {
|
|
20629
|
+
return [];
|
|
20630
|
+
}
|
|
20631
|
+
return invocations.flatMap((invocation) => {
|
|
20632
|
+
if (invocation.tool !== "git" || invocation.dir === undefined)
|
|
20633
|
+
return [];
|
|
20634
|
+
const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
|
|
20635
|
+
const sub = invocation.args[at] ?? "";
|
|
20636
|
+
const rest = invocation.args.slice(at + 1);
|
|
20637
|
+
if (!MUTATING_GIT.has(sub) || inert(rest, sub))
|
|
20638
|
+
return [];
|
|
20639
|
+
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20640
|
+
return [];
|
|
20641
|
+
return [path23.resolve(cwd, invocation.dir ?? ".")];
|
|
20642
|
+
});
|
|
20643
|
+
};
|
|
20644
|
+
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20645
|
+
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20646
|
+
var POPD2 = new Set(["popd", "pop-location"]);
|
|
20647
|
+
var cdInto = (dir, words) => {
|
|
20648
|
+
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
20649
|
+
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20650
|
+
return;
|
|
20651
|
+
return target === undefined ? os7.homedir() : path23.resolve(dir, target);
|
|
20652
|
+
};
|
|
20653
|
+
var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
20654
|
+
var segmentTargets = (cwd, command, dialect) => {
|
|
20655
|
+
const targets = [];
|
|
20656
|
+
let dir = cwd;
|
|
20657
|
+
let stack = [];
|
|
20658
|
+
const groups = [];
|
|
20659
|
+
const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
|
|
20660
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
20661
|
+
if (segment.group === "open") {
|
|
20662
|
+
groups.push({ dir, stack: [...stack] });
|
|
20663
|
+
continue;
|
|
20664
|
+
}
|
|
20665
|
+
if (segment.group === "close") {
|
|
20666
|
+
const saved = groups.pop();
|
|
20667
|
+
if (saved)
|
|
20668
|
+
({ dir, stack } = saved);
|
|
20669
|
+
continue;
|
|
20670
|
+
}
|
|
20671
|
+
const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
|
|
20672
|
+
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
20673
|
+
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
20674
|
+
if (CD2.has(name)) {
|
|
20675
|
+
dir = cdInto(dir, words);
|
|
20676
|
+
continue;
|
|
20677
|
+
}
|
|
20678
|
+
if (PUSHD2.has(name)) {
|
|
20679
|
+
stack.push(dir);
|
|
20680
|
+
dir = cdInto(dir, words);
|
|
20681
|
+
continue;
|
|
20682
|
+
}
|
|
20683
|
+
if (POPD2.has(name)) {
|
|
20684
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
20685
|
+
continue;
|
|
20686
|
+
}
|
|
20687
|
+
for (const file of segment.redirects)
|
|
20688
|
+
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
20689
|
+
const resolved = at(file);
|
|
20690
|
+
if (resolved)
|
|
20691
|
+
targets.push(resolved);
|
|
20692
|
+
}
|
|
20693
|
+
const edits = editsOf(segment.words);
|
|
20694
|
+
if (edits === null) {
|
|
20695
|
+
if (dir !== undefined)
|
|
20696
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20697
|
+
} else
|
|
20698
|
+
for (const file of edits) {
|
|
20699
|
+
const resolved = at(file);
|
|
20700
|
+
if (resolved)
|
|
20701
|
+
targets.push(resolved);
|
|
20702
|
+
}
|
|
20703
|
+
if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
|
|
20704
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20705
|
+
}
|
|
20706
|
+
return targets;
|
|
20707
|
+
};
|
|
20708
|
+
var overridden = (command, dialect) => {
|
|
20709
|
+
const segments = segmentsOf(command, dialect);
|
|
20710
|
+
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
20711
|
+
return false;
|
|
20712
|
+
const words = segments[0].words;
|
|
20713
|
+
const lead = words.findIndex((word) => !/^\w+=/.test(word));
|
|
20714
|
+
return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
|
|
20715
|
+
};
|
|
20716
|
+
var HOST_LABELS = {
|
|
20717
|
+
claude_code: "Claude Code",
|
|
20718
|
+
opencode: "OpenCode",
|
|
20719
|
+
codex_cli: "Codex CLI",
|
|
20720
|
+
codex_desktop: "Codex Desktop",
|
|
20721
|
+
cursor: "Cursor",
|
|
20722
|
+
pi: "Pi"
|
|
20723
|
+
};
|
|
20724
|
+
var ageText = (ms) => {
|
|
20725
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
20726
|
+
return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
|
|
20727
|
+
};
|
|
20728
|
+
var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
20729
|
+
const unblock = `Work in your own worktree (\`git worktree add <path> -b <branch>\`, or \`workit fanout worktree create <slice>\`). Only if the user confirms that session is finished: release it with \`rm '${file}'\`, or prefix one command with ${OVERRIDE}.`;
|
|
20730
|
+
return {
|
|
20731
|
+
kind: "deny",
|
|
20732
|
+
reason: `workit live-session guard: ${checkout} belongs to another live agent session, ${HOST_LABELS[marker.host] ?? marker.host} session ${marker.session}${marker.pid === null ? "" : ` (pid ${marker.pid})`}, last active ${ageText(ageMs)} ago. Do not edit a checkout another session is working in; read-only commands stay allowed. ${unblock}`,
|
|
20733
|
+
unblock
|
|
20734
|
+
};
|
|
20735
|
+
};
|
|
20736
|
+
var guarded = (targets) => {
|
|
20737
|
+
const seen = new Set;
|
|
20738
|
+
return targets.filter((target) => {
|
|
20739
|
+
const dir = path23.dirname(target);
|
|
20740
|
+
if (seen.has(dir))
|
|
20741
|
+
return false;
|
|
20742
|
+
seen.add(dir);
|
|
20743
|
+
return true;
|
|
20744
|
+
});
|
|
20745
|
+
};
|
|
20746
|
+
var liveGate = (owner, targets, deps) => {
|
|
20747
|
+
try {
|
|
20748
|
+
for (const target of guarded(targets)) {
|
|
20749
|
+
const conflict = liveConflict(target, owner, deps);
|
|
20750
|
+
if (conflict && workspaceLiveSessionGuard(conflict.checkout))
|
|
20751
|
+
return liveDeny(conflict);
|
|
20752
|
+
}
|
|
20753
|
+
} catch {}
|
|
20754
|
+
return NONE3;
|
|
20755
|
+
};
|
|
20756
|
+
var claimAll = (owner, targets, deps) => {
|
|
20757
|
+
for (const target of guarded(targets))
|
|
20758
|
+
claimLiveSession(target, owner, deps);
|
|
20759
|
+
};
|
|
20760
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)];
|
|
20761
|
+
function writeGate(cwd, paths, owner, deps) {
|
|
20762
|
+
if (!owner)
|
|
20763
|
+
return beforeWriteGate(cwd, paths);
|
|
20764
|
+
const targets = liveTargets(cwd, paths);
|
|
20765
|
+
const live = liveGate(owner, targets, deps);
|
|
20766
|
+
if (live.kind !== "none")
|
|
20767
|
+
return live;
|
|
20768
|
+
const decision = beforeWriteGate(cwd, paths);
|
|
20769
|
+
if (decision.kind === "none")
|
|
20770
|
+
claimAll(owner, targets, deps);
|
|
20771
|
+
return decision;
|
|
20772
|
+
}
|
|
20773
|
+
function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
20774
|
+
const writes = shellWrites(command);
|
|
20775
|
+
let targets = [];
|
|
20776
|
+
if (owner) {
|
|
20777
|
+
try {
|
|
20778
|
+
targets = [
|
|
20779
|
+
...segmentTargets(cwd, command, dialect),
|
|
20780
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
|
|
20781
|
+
];
|
|
20782
|
+
} catch {
|
|
20783
|
+
targets = [];
|
|
20784
|
+
}
|
|
20785
|
+
}
|
|
20786
|
+
if (owner && targets.length) {
|
|
20787
|
+
const live = liveGate(owner, targets, deps);
|
|
20788
|
+
if (live.kind !== "none") {
|
|
20789
|
+
if (!overridden(command, dialect))
|
|
20790
|
+
return live;
|
|
20791
|
+
noteOverride(cwd, owner, command);
|
|
20792
|
+
}
|
|
20793
|
+
}
|
|
20794
|
+
const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
|
|
20795
|
+
if (owner && targets.length && decision.kind === "none")
|
|
20796
|
+
claimAll(owner, targets, deps);
|
|
20797
|
+
return decision;
|
|
20798
|
+
}
|
|
20799
|
+
var noteOverride = (cwd, owner, command) => {
|
|
20800
|
+
try {
|
|
20801
|
+
if (!inWorkitWorkspace(cwd))
|
|
20802
|
+
return;
|
|
20803
|
+
appendHookObserved(cwd, {
|
|
20804
|
+
type: "live_session.override",
|
|
20805
|
+
actor: { host: owner.host, session: owner.session, agentId: null },
|
|
20806
|
+
session: owner.session,
|
|
20807
|
+
command: command.slice(0, 200)
|
|
20808
|
+
});
|
|
20809
|
+
} catch {}
|
|
20810
|
+
};
|
|
20811
|
+
function beforeWriteGate(cwd, paths) {
|
|
20380
20812
|
try {
|
|
20381
20813
|
const gate = gateFor(cwd);
|
|
20382
20814
|
if (!gate || gate.blockers.length === 0)
|
|
@@ -20394,7 +20826,7 @@ function writeGate(cwd, paths) {
|
|
|
20394
20826
|
}
|
|
20395
20827
|
}
|
|
20396
20828
|
// packages/workit-core/src/hooks/stop.ts
|
|
20397
|
-
import { spawnSync as
|
|
20829
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20398
20830
|
var NONE4 = { kind: "none" };
|
|
20399
20831
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
20400
20832
|
var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
|
|
@@ -20415,7 +20847,7 @@ var asksUser = (message) => {
|
|
|
20415
20847
|
};
|
|
20416
20848
|
var short = (sha) => (sha ?? "?").slice(0, 12);
|
|
20417
20849
|
var gitList = (cwd, args) => {
|
|
20418
|
-
const run =
|
|
20850
|
+
const run = spawnSync14("git", args, {
|
|
20419
20851
|
cwd,
|
|
20420
20852
|
encoding: "utf8",
|
|
20421
20853
|
timeout: 3000,
|
|
@@ -20779,12 +21211,12 @@ ${line}` };
|
|
|
20779
21211
|
};
|
|
20780
21212
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20781
21213
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
20782
|
-
import
|
|
21214
|
+
import path24 from "node:path";
|
|
20783
21215
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
20784
21216
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
20785
21217
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
20786
21218
|
var existingDirectory = (value) => {
|
|
20787
|
-
if (!nonEmpty2(value) || !
|
|
21219
|
+
if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(value))
|
|
20788
21220
|
return null;
|
|
20789
21221
|
try {
|
|
20790
21222
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20929,8 +21361,7 @@ function handleHook(input, deps) {
|
|
|
20929
21361
|
const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
|
|
20930
21362
|
if (raw.kind === "deny")
|
|
20931
21363
|
return raw;
|
|
20932
|
-
const
|
|
20933
|
-
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE5;
|
|
21364
|
+
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
|
|
20934
21365
|
if (gate.kind === "deny")
|
|
20935
21366
|
return gate;
|
|
20936
21367
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
@@ -20943,7 +21374,7 @@ function handleHook(input, deps) {
|
|
|
20943
21374
|
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
20944
21375
|
return NONE5;
|
|
20945
21376
|
case "write.pre":
|
|
20946
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE5;
|
|
21377
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
|
|
20947
21378
|
case "subagent.start":
|
|
20948
21379
|
return {
|
|
20949
21380
|
kind: "context",
|
|
@@ -21522,8 +21953,8 @@ var codexAdapter = {
|
|
|
21522
21953
|
};
|
|
21523
21954
|
|
|
21524
21955
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21525
|
-
import
|
|
21526
|
-
import
|
|
21956
|
+
import fs18 from "node:fs";
|
|
21957
|
+
import path25 from "node:path";
|
|
21527
21958
|
var undocumented = { support: "undocumented", native: null };
|
|
21528
21959
|
var none = { support: "none", native: null };
|
|
21529
21960
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -21720,17 +22151,17 @@ var protocolEvent2 = (input) => {
|
|
|
21720
22151
|
};
|
|
21721
22152
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
21722
22153
|
var lastAssistantText = (file) => {
|
|
21723
|
-
if (!file || !
|
|
22154
|
+
if (!file || !path25.isAbsolute(file))
|
|
21724
22155
|
return null;
|
|
21725
22156
|
try {
|
|
21726
|
-
const size =
|
|
22157
|
+
const size = fs18.statSync(file).size;
|
|
21727
22158
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
21728
22159
|
const buffer = Buffer.alloc(size - start);
|
|
21729
|
-
const fd =
|
|
22160
|
+
const fd = fs18.openSync(file, "r");
|
|
21730
22161
|
try {
|
|
21731
|
-
|
|
22162
|
+
fs18.readSync(fd, buffer, 0, buffer.length, start);
|
|
21732
22163
|
} finally {
|
|
21733
|
-
|
|
22164
|
+
fs18.closeSync(fd);
|
|
21734
22165
|
}
|
|
21735
22166
|
const lines = buffer.toString("utf8").split(`
|
|
21736
22167
|
`);
|
|
@@ -21757,7 +22188,7 @@ var lastAssistantText = (file) => {
|
|
|
21757
22188
|
return null;
|
|
21758
22189
|
}
|
|
21759
22190
|
};
|
|
21760
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
22191
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`);
|
|
21761
22192
|
var hookCwd = (input) => {
|
|
21762
22193
|
const cwd = existingDirectory(input.cwd);
|
|
21763
22194
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|