@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.
|
@@ -9570,24 +9570,26 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
9570
9570
|
}, resolveRuntimeReleaseTracks = (cwd) => {
|
|
9571
9571
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9572
9572
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
9573
|
-
},
|
|
9573
|
+
}, inheritedSetting = (cwd, dir, has) => {
|
|
9574
9574
|
let matched;
|
|
9575
9575
|
try {
|
|
9576
9576
|
matched = matchRuntimeWorkspace(cwd, dir);
|
|
9577
9577
|
} catch {
|
|
9578
|
-
return
|
|
9578
|
+
return null;
|
|
9579
9579
|
}
|
|
9580
9580
|
if (!matched)
|
|
9581
|
-
return
|
|
9581
|
+
return null;
|
|
9582
9582
|
const { selected, matches } = matched;
|
|
9583
9583
|
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
9584
|
-
|
|
9584
|
+
return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
|
|
9585
|
+
}, workspaceTrailers = (cwd, dir) => {
|
|
9586
|
+
const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
|
|
9585
9587
|
return {
|
|
9586
|
-
enabled: source?.trailers !== false,
|
|
9587
|
-
workspace:
|
|
9588
|
-
from: source?.name ?? null
|
|
9588
|
+
enabled: found?.source?.trailers !== false,
|
|
9589
|
+
workspace: found?.workspace ?? null,
|
|
9590
|
+
from: found?.source?.name ?? null
|
|
9589
9591
|
};
|
|
9590
|
-
}, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
9592
|
+
}, workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false, TIER_KEYS, TIER_HOSTS, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
9591
9593
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9592
9594
|
if (!matched)
|
|
9593
9595
|
return null;
|
|
@@ -9698,6 +9700,8 @@ var init_workspaces = __esm(() => {
|
|
|
9698
9700
|
defaultEndpoint: unknown().optional(),
|
|
9699
9701
|
verification: _enum(["self", "independent"]).optional(),
|
|
9700
9702
|
trailers: boolean2().optional(),
|
|
9703
|
+
liveSessionGuard: boolean2().optional(),
|
|
9704
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
9701
9705
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
9702
9706
|
kind,
|
|
9703
9707
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -9747,6 +9751,17 @@ var init_workspaces = __esm(() => {
|
|
|
9747
9751
|
runtimeWorkspaceIndexSchema = object({
|
|
9748
9752
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
9749
9753
|
}).passthrough();
|
|
9754
|
+
TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
9755
|
+
TIER_HOSTS = new Set([
|
|
9756
|
+
"claude_code",
|
|
9757
|
+
"opencode",
|
|
9758
|
+
"codex",
|
|
9759
|
+
"codex_cli",
|
|
9760
|
+
"codex_desktop",
|
|
9761
|
+
"cursor",
|
|
9762
|
+
"pi",
|
|
9763
|
+
"default"
|
|
9764
|
+
]);
|
|
9750
9765
|
});
|
|
9751
9766
|
|
|
9752
9767
|
// packages/workit-core/src/core/release-tracks.ts
|
|
@@ -20052,10 +20067,238 @@ var init_policy = __esm(() => {
|
|
|
20052
20067
|
init_shell_words();
|
|
20053
20068
|
});
|
|
20054
20069
|
|
|
20070
|
+
// packages/workit-core/src/core/live-session.ts
|
|
20071
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20072
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
20073
|
+
import fs17 from "node:fs";
|
|
20074
|
+
import os6 from "node:os";
|
|
20075
|
+
import path22 from "node:path";
|
|
20076
|
+
var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session, familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, ""), processAlive = (pid) => {
|
|
20077
|
+
try {
|
|
20078
|
+
process.kill(pid, 0);
|
|
20079
|
+
return true;
|
|
20080
|
+
} catch (error) {
|
|
20081
|
+
return error.code === "EPERM";
|
|
20082
|
+
}
|
|
20083
|
+
}, processInfo = (pid) => {
|
|
20084
|
+
try {
|
|
20085
|
+
if (liveProbe.platform === "linux") {
|
|
20086
|
+
const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
20087
|
+
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
20088
|
+
const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
20089
|
+
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
20090
|
+
}
|
|
20091
|
+
if (liveProbe.platform === "win32")
|
|
20092
|
+
return null;
|
|
20093
|
+
const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
|
|
20094
|
+
return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
|
|
20095
|
+
} catch {
|
|
20096
|
+
return null;
|
|
20097
|
+
}
|
|
20098
|
+
}, WRAPPER, PIDS, hostPid = (transport) => {
|
|
20099
|
+
if (transport !== "hook-process")
|
|
20100
|
+
return process.pid;
|
|
20101
|
+
const key = `${liveProbe.platform}\x00${process.ppid}`;
|
|
20102
|
+
if (PIDS.has(key))
|
|
20103
|
+
return PIDS.get(key) ?? null;
|
|
20104
|
+
if (liveProbe.platform === "win32") {
|
|
20105
|
+
PIDS.set(key, null);
|
|
20106
|
+
return null;
|
|
20107
|
+
}
|
|
20108
|
+
let pid = process.ppid;
|
|
20109
|
+
for (let hop = 0;hop < 4; hop++) {
|
|
20110
|
+
const info = processInfo(pid);
|
|
20111
|
+
if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
|
|
20112
|
+
break;
|
|
20113
|
+
pid = info.ppid;
|
|
20114
|
+
}
|
|
20115
|
+
PIDS.set(key, pid);
|
|
20116
|
+
return pid;
|
|
20117
|
+
}, sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-"), liveOwner = (input, transport, env = process.env) => {
|
|
20118
|
+
const session = input.session.id?.trim();
|
|
20119
|
+
if (!session)
|
|
20120
|
+
return null;
|
|
20121
|
+
const leads = new Set([leadOf(sessionSafe(session))]);
|
|
20122
|
+
if (input.session.parentId)
|
|
20123
|
+
leads.add(leadOf(sessionSafe(input.session.parentId)));
|
|
20124
|
+
if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
|
|
20125
|
+
leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
|
|
20126
|
+
return {
|
|
20127
|
+
host: input.host,
|
|
20128
|
+
session: sessionSafe(session),
|
|
20129
|
+
leads: [...leads],
|
|
20130
|
+
pid: () => hostPid(transport)
|
|
20131
|
+
};
|
|
20132
|
+
}, checkoutOf = (target) => {
|
|
20133
|
+
let dir = path22.resolve(target);
|
|
20134
|
+
while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
|
|
20135
|
+
dir = path22.dirname(dir);
|
|
20136
|
+
if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
|
|
20137
|
+
dir = path22.dirname(dir);
|
|
20138
|
+
const parts = dir.split(path22.sep);
|
|
20139
|
+
const dotGit = parts.lastIndexOf(".git");
|
|
20140
|
+
if (dotGit > 0)
|
|
20141
|
+
dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
|
|
20142
|
+
const location = resolveStore(dir);
|
|
20143
|
+
if (location instanceof Error || !location.shared || !location.top)
|
|
20144
|
+
return null;
|
|
20145
|
+
let top = location.top;
|
|
20146
|
+
try {
|
|
20147
|
+
top = fs17.realpathSync.native(top);
|
|
20148
|
+
} catch {}
|
|
20149
|
+
return top;
|
|
20150
|
+
}, markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
|
|
20151
|
+
let key = path22.resolve(top);
|
|
20152
|
+
try {
|
|
20153
|
+
key = fs17.realpathSync.native(key);
|
|
20154
|
+
} catch {}
|
|
20155
|
+
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
20156
|
+
}, readMarker = (file) => {
|
|
20157
|
+
try {
|
|
20158
|
+
const value = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
20159
|
+
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
20160
|
+
return null;
|
|
20161
|
+
return {
|
|
20162
|
+
version: 1,
|
|
20163
|
+
session: value.session,
|
|
20164
|
+
leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
|
|
20165
|
+
host: value.host,
|
|
20166
|
+
pid: typeof value.pid === "number" ? value.pid : null,
|
|
20167
|
+
pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
|
|
20168
|
+
machine: typeof value.machine === "string" ? value.machine : "",
|
|
20169
|
+
checkout: typeof value.checkout === "string" ? value.checkout : "",
|
|
20170
|
+
heartbeatAt: value.heartbeatAt
|
|
20171
|
+
};
|
|
20172
|
+
} catch {
|
|
20173
|
+
return null;
|
|
20174
|
+
}
|
|
20175
|
+
}, ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt), isLive = (marker, deps) => {
|
|
20176
|
+
const age = ageOf2(marker, deps.now ?? new Date);
|
|
20177
|
+
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
20178
|
+
return false;
|
|
20179
|
+
if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
|
|
20180
|
+
return true;
|
|
20181
|
+
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
20182
|
+
return false;
|
|
20183
|
+
if (marker.pidStart === null)
|
|
20184
|
+
return true;
|
|
20185
|
+
const start = liveProbe.start(marker.pid);
|
|
20186
|
+
return start === null || start === marker.pidStart;
|
|
20187
|
+
}, sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead)), sameOwner = (marker, owner, machine) => {
|
|
20188
|
+
if (sameLead(marker, owner))
|
|
20189
|
+
return true;
|
|
20190
|
+
if (marker.machine !== machine)
|
|
20191
|
+
return false;
|
|
20192
|
+
const pid = owner.pid();
|
|
20193
|
+
if (pid === null || marker.pid === null || marker.pidStart === null)
|
|
20194
|
+
return familyOf(marker.host) === familyOf(owner.host);
|
|
20195
|
+
return marker.pid === pid;
|
|
20196
|
+
}, liveConflict = (target, owner, deps = {}) => {
|
|
20197
|
+
try {
|
|
20198
|
+
const checkout = checkoutOf(target);
|
|
20199
|
+
if (!checkout)
|
|
20200
|
+
return null;
|
|
20201
|
+
const file = markerFile2(checkout);
|
|
20202
|
+
const marker = readMarker(file);
|
|
20203
|
+
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
20204
|
+
return null;
|
|
20205
|
+
if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
|
|
20206
|
+
return null;
|
|
20207
|
+
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
20208
|
+
} catch {
|
|
20209
|
+
return null;
|
|
20210
|
+
}
|
|
20211
|
+
}, claimLiveSession = (target, owner, deps = {}) => {
|
|
20212
|
+
try {
|
|
20213
|
+
const checkout = checkoutOf(target);
|
|
20214
|
+
if (!checkout)
|
|
20215
|
+
return;
|
|
20216
|
+
const file = markerFile2(checkout);
|
|
20217
|
+
const now = deps.now ?? new Date;
|
|
20218
|
+
const machine = deps.machine ?? os6.hostname();
|
|
20219
|
+
const current = readMarker(file);
|
|
20220
|
+
if (current) {
|
|
20221
|
+
const age = ageOf2(current, now);
|
|
20222
|
+
if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
|
|
20223
|
+
return;
|
|
20224
|
+
if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
|
|
20225
|
+
return;
|
|
20226
|
+
}
|
|
20227
|
+
const pid = owner.pid();
|
|
20228
|
+
const marker = {
|
|
20229
|
+
version: 1,
|
|
20230
|
+
session: owner.session,
|
|
20231
|
+
leads: owner.leads,
|
|
20232
|
+
host: owner.host,
|
|
20233
|
+
pid,
|
|
20234
|
+
pidStart: pid === null ? null : liveProbe.start(pid),
|
|
20235
|
+
machine,
|
|
20236
|
+
checkout,
|
|
20237
|
+
heartbeatAt: now.toISOString()
|
|
20238
|
+
};
|
|
20239
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
20240
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
20241
|
+
fs17.writeFileSync(temp, JSON.stringify(marker));
|
|
20242
|
+
fs17.renameSync(temp, file);
|
|
20243
|
+
} catch {}
|
|
20244
|
+
};
|
|
20245
|
+
var init_live_session = __esm(() => {
|
|
20246
|
+
init_paths();
|
|
20247
|
+
init_logger();
|
|
20248
|
+
init_store_lock();
|
|
20249
|
+
LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
20250
|
+
liveProbe = {
|
|
20251
|
+
platform: process.platform,
|
|
20252
|
+
run: (args) => spawnSync13("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
20253
|
+
start: (pid) => processStartOf(pid)
|
|
20254
|
+
};
|
|
20255
|
+
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;
|
|
20256
|
+
PIDS = new Map;
|
|
20257
|
+
});
|
|
20258
|
+
|
|
20055
20259
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
20056
20260
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
20057
|
-
import
|
|
20058
|
-
|
|
20261
|
+
import os7 from "node:os";
|
|
20262
|
+
import path23 from "node:path";
|
|
20263
|
+
function writeGate(cwd, paths, owner, deps) {
|
|
20264
|
+
if (!owner)
|
|
20265
|
+
return beforeWriteGate(cwd, paths);
|
|
20266
|
+
const targets = liveTargets(cwd, paths);
|
|
20267
|
+
const live = liveGate(owner, targets, deps);
|
|
20268
|
+
if (live.kind !== "none")
|
|
20269
|
+
return live;
|
|
20270
|
+
const decision = beforeWriteGate(cwd, paths);
|
|
20271
|
+
if (decision.kind === "none")
|
|
20272
|
+
claimAll(owner, targets, deps);
|
|
20273
|
+
return decision;
|
|
20274
|
+
}
|
|
20275
|
+
function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
20276
|
+
const writes = shellWrites(command);
|
|
20277
|
+
let targets = [];
|
|
20278
|
+
if (owner) {
|
|
20279
|
+
try {
|
|
20280
|
+
targets = [
|
|
20281
|
+
...segmentTargets(cwd, command, dialect),
|
|
20282
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
|
|
20283
|
+
];
|
|
20284
|
+
} catch {
|
|
20285
|
+
targets = [];
|
|
20286
|
+
}
|
|
20287
|
+
}
|
|
20288
|
+
if (owner && targets.length) {
|
|
20289
|
+
const live = liveGate(owner, targets, deps);
|
|
20290
|
+
if (live.kind !== "none") {
|
|
20291
|
+
if (!overridden(command, dialect))
|
|
20292
|
+
return live;
|
|
20293
|
+
noteOverride(cwd, owner, command);
|
|
20294
|
+
}
|
|
20295
|
+
}
|
|
20296
|
+
const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
|
|
20297
|
+
if (owner && targets.length && decision.kind === "none")
|
|
20298
|
+
claimAll(owner, targets, deps);
|
|
20299
|
+
return decision;
|
|
20300
|
+
}
|
|
20301
|
+
function beforeWriteGate(cwd, paths) {
|
|
20059
20302
|
try {
|
|
20060
20303
|
const gate = gateFor(cwd);
|
|
20061
20304
|
if (!gate || gate.blockers.length === 0)
|
|
@@ -20074,21 +20317,21 @@ function writeGate(cwd, paths) {
|
|
|
20074
20317
|
}
|
|
20075
20318
|
var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
20076
20319
|
let rest = "";
|
|
20077
|
-
for (let dir =
|
|
20320
|
+
for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
|
|
20078
20321
|
try {
|
|
20079
|
-
return
|
|
20322
|
+
return path23.join(realpathSync5(dir), rest);
|
|
20080
20323
|
} catch {
|
|
20081
|
-
if (
|
|
20082
|
-
return
|
|
20083
|
-
rest = rest ?
|
|
20324
|
+
if (path23.dirname(dir) === dir)
|
|
20325
|
+
return path23.resolve(file);
|
|
20326
|
+
rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
|
|
20084
20327
|
}
|
|
20085
20328
|
}
|
|
20086
20329
|
}, exempt = (cwd, root, file, plans) => {
|
|
20087
20330
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20088
|
-
const relative =
|
|
20089
|
-
if (relative.startsWith("..") ||
|
|
20331
|
+
const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
|
|
20332
|
+
if (relative.startsWith("..") || path23.isAbsolute(relative))
|
|
20090
20333
|
return true;
|
|
20091
|
-
const posix = relative.split(
|
|
20334
|
+
const posix = relative.split(path23.sep).join("/");
|
|
20092
20335
|
const segments = posix.split("/");
|
|
20093
20336
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20094
20337
|
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
|
|
@@ -20180,7 +20423,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20180
20423
|
if (!unknown && targets.length === 0)
|
|
20181
20424
|
return { writes: false, targets: null };
|
|
20182
20425
|
return { writes: true, targets: unknown ? null : targets };
|
|
20183
|
-
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20426
|
+
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
20184
20427
|
const store = new TaskStore(cwd);
|
|
20185
20428
|
const listed = store.listTaskIndex();
|
|
20186
20429
|
if (!listed.ok)
|
|
@@ -20216,21 +20459,182 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20216
20459
|
if (cache2.size > 32)
|
|
20217
20460
|
cache2.delete(cache2.keys().next().value);
|
|
20218
20461
|
return gate;
|
|
20462
|
+
}, MUTATING_GIT, DRY_N, MUTATING_WORKIT_GIT, DIR_TARGET = ".workit-live-target", OVERRIDE = "WORKIT_LIVE_SESSION=override", 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("--")), gitMutationDirs = (cwd, command, dialect) => {
|
|
20463
|
+
let invocations;
|
|
20464
|
+
try {
|
|
20465
|
+
invocations = rawInvocations(command, dialect);
|
|
20466
|
+
} catch {
|
|
20467
|
+
return [];
|
|
20468
|
+
}
|
|
20469
|
+
return invocations.flatMap((invocation) => {
|
|
20470
|
+
if (invocation.tool !== "git" || invocation.dir === undefined)
|
|
20471
|
+
return [];
|
|
20472
|
+
const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
|
|
20473
|
+
const sub = invocation.args[at] ?? "";
|
|
20474
|
+
const rest = invocation.args.slice(at + 1);
|
|
20475
|
+
if (!MUTATING_GIT.has(sub) || inert(rest, sub))
|
|
20476
|
+
return [];
|
|
20477
|
+
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20478
|
+
return [];
|
|
20479
|
+
return [path23.resolve(cwd, invocation.dir ?? ".")];
|
|
20480
|
+
});
|
|
20481
|
+
}, CD2, PUSHD2, POPD2, cdInto = (dir, words) => {
|
|
20482
|
+
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
20483
|
+
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20484
|
+
return;
|
|
20485
|
+
return target === undefined ? os7.homedir() : path23.resolve(dir, target);
|
|
20486
|
+
}, quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`, segmentTargets = (cwd, command, dialect) => {
|
|
20487
|
+
const targets = [];
|
|
20488
|
+
let dir = cwd;
|
|
20489
|
+
let stack = [];
|
|
20490
|
+
const groups = [];
|
|
20491
|
+
const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
|
|
20492
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
20493
|
+
if (segment.group === "open") {
|
|
20494
|
+
groups.push({ dir, stack: [...stack] });
|
|
20495
|
+
continue;
|
|
20496
|
+
}
|
|
20497
|
+
if (segment.group === "close") {
|
|
20498
|
+
const saved = groups.pop();
|
|
20499
|
+
if (saved)
|
|
20500
|
+
({ dir, stack } = saved);
|
|
20501
|
+
continue;
|
|
20502
|
+
}
|
|
20503
|
+
const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
|
|
20504
|
+
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
20505
|
+
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
20506
|
+
if (CD2.has(name)) {
|
|
20507
|
+
dir = cdInto(dir, words);
|
|
20508
|
+
continue;
|
|
20509
|
+
}
|
|
20510
|
+
if (PUSHD2.has(name)) {
|
|
20511
|
+
stack.push(dir);
|
|
20512
|
+
dir = cdInto(dir, words);
|
|
20513
|
+
continue;
|
|
20514
|
+
}
|
|
20515
|
+
if (POPD2.has(name)) {
|
|
20516
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
20517
|
+
continue;
|
|
20518
|
+
}
|
|
20519
|
+
for (const file of segment.redirects)
|
|
20520
|
+
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
20521
|
+
const resolved = at(file);
|
|
20522
|
+
if (resolved)
|
|
20523
|
+
targets.push(resolved);
|
|
20524
|
+
}
|
|
20525
|
+
const edits = editsOf(segment.words);
|
|
20526
|
+
if (edits === null) {
|
|
20527
|
+
if (dir !== undefined)
|
|
20528
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20529
|
+
} else
|
|
20530
|
+
for (const file of edits) {
|
|
20531
|
+
const resolved = at(file);
|
|
20532
|
+
if (resolved)
|
|
20533
|
+
targets.push(resolved);
|
|
20534
|
+
}
|
|
20535
|
+
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), "")))
|
|
20536
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20537
|
+
}
|
|
20538
|
+
return targets;
|
|
20539
|
+
}, overridden = (command, dialect) => {
|
|
20540
|
+
const segments = segmentsOf(command, dialect);
|
|
20541
|
+
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
20542
|
+
return false;
|
|
20543
|
+
const words = segments[0].words;
|
|
20544
|
+
const lead = words.findIndex((word) => !/^\w+=/.test(word));
|
|
20545
|
+
return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
|
|
20546
|
+
}, HOST_LABELS, ageText = (ms) => {
|
|
20547
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
20548
|
+
return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
|
|
20549
|
+
}, liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
20550
|
+
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}.`;
|
|
20551
|
+
return {
|
|
20552
|
+
kind: "deny",
|
|
20553
|
+
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}`,
|
|
20554
|
+
unblock
|
|
20555
|
+
};
|
|
20556
|
+
}, guarded = (targets) => {
|
|
20557
|
+
const seen = new Set;
|
|
20558
|
+
return targets.filter((target) => {
|
|
20559
|
+
const dir = path23.dirname(target);
|
|
20560
|
+
if (seen.has(dir))
|
|
20561
|
+
return false;
|
|
20562
|
+
seen.add(dir);
|
|
20563
|
+
return true;
|
|
20564
|
+
});
|
|
20565
|
+
}, liveGate = (owner, targets, deps) => {
|
|
20566
|
+
try {
|
|
20567
|
+
for (const target of guarded(targets)) {
|
|
20568
|
+
const conflict = liveConflict(target, owner, deps);
|
|
20569
|
+
if (conflict && workspaceLiveSessionGuard(conflict.checkout))
|
|
20570
|
+
return liveDeny(conflict);
|
|
20571
|
+
}
|
|
20572
|
+
} catch {}
|
|
20573
|
+
return NONE3;
|
|
20574
|
+
}, claimAll = (owner, targets, deps) => {
|
|
20575
|
+
for (const target of guarded(targets))
|
|
20576
|
+
claimLiveSession(target, owner, deps);
|
|
20577
|
+
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)], noteOverride = (cwd, owner, command) => {
|
|
20578
|
+
try {
|
|
20579
|
+
if (!inWorkitWorkspace(cwd))
|
|
20580
|
+
return;
|
|
20581
|
+
appendHookObserved(cwd, {
|
|
20582
|
+
type: "live_session.override",
|
|
20583
|
+
actor: { host: owner.host, session: owner.session, agentId: null },
|
|
20584
|
+
session: owner.session,
|
|
20585
|
+
command: command.slice(0, 200)
|
|
20586
|
+
});
|
|
20587
|
+
} catch {}
|
|
20219
20588
|
};
|
|
20220
20589
|
var init_write_gate = __esm(() => {
|
|
20221
20590
|
init_task_store();
|
|
20222
20591
|
init_derive();
|
|
20223
20592
|
init_task_evaluation();
|
|
20593
|
+
init_live_session();
|
|
20594
|
+
init_workspaces();
|
|
20224
20595
|
init_ledger();
|
|
20596
|
+
init_raw_git();
|
|
20225
20597
|
init_shell_words();
|
|
20226
20598
|
NONE3 = { kind: "none" };
|
|
20227
20599
|
CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20228
20600
|
PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
20229
20601
|
cache2 = new Map;
|
|
20602
|
+
MUTATING_GIT = new Set([
|
|
20603
|
+
"add",
|
|
20604
|
+
"am",
|
|
20605
|
+
"apply",
|
|
20606
|
+
"checkout",
|
|
20607
|
+
"cherry-pick",
|
|
20608
|
+
"clean",
|
|
20609
|
+
"commit",
|
|
20610
|
+
"merge",
|
|
20611
|
+
"mv",
|
|
20612
|
+
"pull",
|
|
20613
|
+
"rebase",
|
|
20614
|
+
"reset",
|
|
20615
|
+
"restore",
|
|
20616
|
+
"revert",
|
|
20617
|
+
"rm",
|
|
20618
|
+
"stash",
|
|
20619
|
+
"switch"
|
|
20620
|
+
]);
|
|
20621
|
+
DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
20622
|
+
MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
20623
|
+
CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20624
|
+
PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20625
|
+
POPD2 = new Set(["popd", "pop-location"]);
|
|
20626
|
+
HOST_LABELS = {
|
|
20627
|
+
claude_code: "Claude Code",
|
|
20628
|
+
opencode: "OpenCode",
|
|
20629
|
+
codex_cli: "Codex CLI",
|
|
20630
|
+
codex_desktop: "Codex Desktop",
|
|
20631
|
+
cursor: "Cursor",
|
|
20632
|
+
pi: "Pi"
|
|
20633
|
+
};
|
|
20230
20634
|
});
|
|
20231
20635
|
|
|
20232
20636
|
// packages/workit-core/src/hooks/stop.ts
|
|
20233
|
-
import { spawnSync as
|
|
20637
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20234
20638
|
function stopObligation(input, descriptor) {
|
|
20235
20639
|
try {
|
|
20236
20640
|
const session = input.session.id;
|
|
@@ -20294,7 +20698,7 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
|
|
|
20294
20698
|
`);
|
|
20295
20699
|
return QUESTION.test(tail) || OFFER.test(tail) || YES_NO.test(tail);
|
|
20296
20700
|
}, short = (sha) => (sha ?? "?").slice(0, 12), gitList = (cwd, args) => {
|
|
20297
|
-
const run =
|
|
20701
|
+
const run = spawnSync14("git", args, {
|
|
20298
20702
|
cwd,
|
|
20299
20703
|
encoding: "utf8",
|
|
20300
20704
|
timeout: 3000,
|
|
@@ -20606,9 +21010,9 @@ var init_skill_nudge = __esm(() => {
|
|
|
20606
21010
|
|
|
20607
21011
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20608
21012
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
20609
|
-
import
|
|
21013
|
+
import path24 from "node:path";
|
|
20610
21014
|
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) => {
|
|
20611
|
-
if (!nonEmpty2(value) || !
|
|
21015
|
+
if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(value))
|
|
20612
21016
|
return null;
|
|
20613
21017
|
try {
|
|
20614
21018
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20713,8 +21117,7 @@ function handleHook(input, deps) {
|
|
|
20713
21117
|
const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
|
|
20714
21118
|
if (raw.kind === "deny")
|
|
20715
21119
|
return raw;
|
|
20716
|
-
const
|
|
20717
|
-
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE5;
|
|
21120
|
+
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
|
|
20718
21121
|
if (gate.kind === "deny")
|
|
20719
21122
|
return gate;
|
|
20720
21123
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
@@ -20727,7 +21130,7 @@ function handleHook(input, deps) {
|
|
|
20727
21130
|
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
20728
21131
|
return NONE5;
|
|
20729
21132
|
case "write.pre":
|
|
20730
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE5;
|
|
21133
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
|
|
20731
21134
|
case "subagent.start":
|
|
20732
21135
|
return {
|
|
20733
21136
|
kind: "context",
|
|
@@ -20810,6 +21213,7 @@ var init_handle = __esm(() => {
|
|
|
20810
21213
|
init_raw_git();
|
|
20811
21214
|
init_skill_nudge();
|
|
20812
21215
|
init_stop();
|
|
21216
|
+
init_live_session();
|
|
20813
21217
|
init_write_gate();
|
|
20814
21218
|
NONE5 = { kind: "none" };
|
|
20815
21219
|
offered = new Set;
|
|
@@ -21348,8 +21752,8 @@ var init_codex = __esm(() => {
|
|
|
21348
21752
|
});
|
|
21349
21753
|
|
|
21350
21754
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21351
|
-
import
|
|
21352
|
-
import
|
|
21755
|
+
import fs18 from "node:fs";
|
|
21756
|
+
import path25 from "node:path";
|
|
21353
21757
|
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
21354
21758
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
21355
21759
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -21430,17 +21834,17 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
21430
21834
|
};
|
|
21431
21835
|
}
|
|
21432
21836
|
}, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
|
|
21433
|
-
if (!file || !
|
|
21837
|
+
if (!file || !path25.isAbsolute(file))
|
|
21434
21838
|
return null;
|
|
21435
21839
|
try {
|
|
21436
|
-
const size =
|
|
21840
|
+
const size = fs18.statSync(file).size;
|
|
21437
21841
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
21438
21842
|
const buffer = Buffer.alloc(size - start);
|
|
21439
|
-
const fd =
|
|
21843
|
+
const fd = fs18.openSync(file, "r");
|
|
21440
21844
|
try {
|
|
21441
|
-
|
|
21845
|
+
fs18.readSync(fd, buffer, 0, buffer.length, start);
|
|
21442
21846
|
} finally {
|
|
21443
|
-
|
|
21847
|
+
fs18.closeSync(fd);
|
|
21444
21848
|
}
|
|
21445
21849
|
const lines = buffer.toString("utf8").split(`
|
|
21446
21850
|
`);
|
|
@@ -21466,7 +21870,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
21466
21870
|
} catch {
|
|
21467
21871
|
return null;
|
|
21468
21872
|
}
|
|
21469
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
21873
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`), hookCwd = (input) => {
|
|
21470
21874
|
const cwd = existingDirectory(input.cwd);
|
|
21471
21875
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
21472
21876
|
return cwd;
|
|
@@ -21692,6 +22096,7 @@ var init_hooks = __esm(async () => {
|
|
|
21692
22096
|
init_context();
|
|
21693
22097
|
init_policy();
|
|
21694
22098
|
init_write_gate();
|
|
22099
|
+
init_live_session();
|
|
21695
22100
|
init_stop();
|
|
21696
22101
|
init_raw_git();
|
|
21697
22102
|
init_skill_nudge();
|