@brainervirus/workit-codex 8.4.0 → 8.5.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.
|
@@ -24,6 +24,8 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
24
24
|
};
|
|
25
25
|
});
|
|
26
26
|
// packages/workit-core/src/hooks/context.ts
|
|
27
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
28
|
+
import fs13 from "node:fs";
|
|
27
29
|
import path16 from "node:path";
|
|
28
30
|
|
|
29
31
|
// packages/workit-core/src/core/methods.ts
|
|
@@ -134,8 +136,288 @@ Skills: load the skill, never act from memory of it (slash aliases /wk-<name>).
|
|
|
134
136
|
- architecture, deepen modules (user-invoked; offer it): workit-architecture
|
|
135
137
|
`.trim();
|
|
136
138
|
|
|
139
|
+
// packages/workit-core/src/store/paths.ts
|
|
140
|
+
import { spawnSync } from "node:child_process";
|
|
141
|
+
import { createHash } from "node:crypto";
|
|
142
|
+
import fs from "node:fs";
|
|
143
|
+
import path from "node:path";
|
|
144
|
+
var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
|
|
145
|
+
var gitRun = (cwd, args) => {
|
|
146
|
+
const run = spawnSync("git", args, {
|
|
147
|
+
cwd,
|
|
148
|
+
encoding: "utf8",
|
|
149
|
+
timeout: 20000,
|
|
150
|
+
killSignal: "SIGKILL",
|
|
151
|
+
windowsHide: true,
|
|
152
|
+
env: {
|
|
153
|
+
...process.env,
|
|
154
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
155
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
156
|
+
LC_ALL: "C",
|
|
157
|
+
LANGUAGE: "C"
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
const error = run.error;
|
|
161
|
+
return {
|
|
162
|
+
ok: run.status === 0,
|
|
163
|
+
stdout: run.stdout ?? "",
|
|
164
|
+
stderr: error ? String(error.message) : run.stderr ?? "",
|
|
165
|
+
missing: error?.code === "ENOENT"
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
var shortHash = (value) => createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
169
|
+
var real = (file) => {
|
|
170
|
+
try {
|
|
171
|
+
return fs.realpathSync.native(file);
|
|
172
|
+
} catch {
|
|
173
|
+
return file;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var DISCOVERY_ENV = [
|
|
177
|
+
"GIT_DIR",
|
|
178
|
+
"GIT_COMMON_DIR",
|
|
179
|
+
"GIT_WORK_TREE",
|
|
180
|
+
"GIT_CEILING_DIRECTORIES",
|
|
181
|
+
"GIT_DISCOVERY_ACROSS_FILESYSTEM"
|
|
182
|
+
];
|
|
183
|
+
var isGitDir = (dir) => {
|
|
184
|
+
try {
|
|
185
|
+
return fs.statSync(path.join(dir, "HEAD")).isFile() && fs.statSync(path.join(dir, "objects")).isDirectory() && fs.statSync(path.join(dir, "refs")).isDirectory();
|
|
186
|
+
} catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var reftableRefs = (common) => {
|
|
191
|
+
try {
|
|
192
|
+
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs.readFileSync(path.join(common, "config"), "utf8"));
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
function fastGit(root) {
|
|
198
|
+
if (DISCOVERY_ENV.some((name) => process.env[name]))
|
|
199
|
+
return null;
|
|
200
|
+
let dir = real(root);
|
|
201
|
+
let device = null;
|
|
202
|
+
for (;; ) {
|
|
203
|
+
let stat;
|
|
204
|
+
try {
|
|
205
|
+
stat = fs.statSync(dir);
|
|
206
|
+
} catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
if (device !== null && stat.dev !== device)
|
|
210
|
+
return { kind: "none" };
|
|
211
|
+
device = stat.dev;
|
|
212
|
+
const dotGit = path.join(dir, ".git");
|
|
213
|
+
let entry = null;
|
|
214
|
+
try {
|
|
215
|
+
entry = fs.statSync(dotGit);
|
|
216
|
+
} catch {}
|
|
217
|
+
let gitDir = null;
|
|
218
|
+
let top = null;
|
|
219
|
+
if (entry) {
|
|
220
|
+
top = dir;
|
|
221
|
+
if (entry.isDirectory())
|
|
222
|
+
gitDir = dotGit;
|
|
223
|
+
else if (entry.isFile()) {
|
|
224
|
+
let text = "";
|
|
225
|
+
try {
|
|
226
|
+
text = fs.readFileSync(dotGit, "utf8");
|
|
227
|
+
} catch {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
231
|
+
if (!match)
|
|
232
|
+
return null;
|
|
233
|
+
gitDir = path.resolve(dir, match[1].trim());
|
|
234
|
+
} else
|
|
235
|
+
return null;
|
|
236
|
+
if (!fs.existsSync(path.join(gitDir, "HEAD")))
|
|
237
|
+
return null;
|
|
238
|
+
} else if (isGitDir(dir))
|
|
239
|
+
gitDir = dir;
|
|
240
|
+
if (gitDir) {
|
|
241
|
+
let common = gitDir;
|
|
242
|
+
try {
|
|
243
|
+
common = path.resolve(gitDir, fs.readFileSync(path.join(gitDir, "commondir"), "utf8").trim());
|
|
244
|
+
} catch {}
|
|
245
|
+
if (!fs.existsSync(path.join(common, "objects")))
|
|
246
|
+
return null;
|
|
247
|
+
return {
|
|
248
|
+
kind: "repo",
|
|
249
|
+
common: real(common),
|
|
250
|
+
gitDir: real(gitDir),
|
|
251
|
+
top: top === null ? null : real(top),
|
|
252
|
+
reftable: reftableRefs(common)
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const parent = path.dirname(dir);
|
|
256
|
+
if (parent === dir)
|
|
257
|
+
return { kind: "none" };
|
|
258
|
+
dir = parent;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function resolveStore(root) {
|
|
262
|
+
root = path.resolve(root);
|
|
263
|
+
let directory = false;
|
|
264
|
+
try {
|
|
265
|
+
directory = fs.statSync(root).isDirectory();
|
|
266
|
+
} catch {}
|
|
267
|
+
if (!directory)
|
|
268
|
+
return { dir: path.join(root, ".workit"), shared: false, top: null };
|
|
269
|
+
const fast = fastGit(root);
|
|
270
|
+
if (fast?.kind === "none")
|
|
271
|
+
return { dir: path.join(root, ".workit"), shared: false, top: null };
|
|
272
|
+
if (fast?.kind === "repo")
|
|
273
|
+
return {
|
|
274
|
+
dir: path.join(fast.common, "workit"),
|
|
275
|
+
shared: true,
|
|
276
|
+
top: fast.top,
|
|
277
|
+
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
278
|
+
};
|
|
279
|
+
const run = gitRun(root, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
280
|
+
const common = run.stdout.trim();
|
|
281
|
+
if (run.ok && common) {
|
|
282
|
+
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
283
|
+
return {
|
|
284
|
+
dir: path.join(real(path.resolve(root, common)), "workit"),
|
|
285
|
+
shared: true,
|
|
286
|
+
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
290
|
+
return { dir: path.join(root, ".workit"), shared: false, top: null };
|
|
291
|
+
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
292
|
+
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
293
|
+
}
|
|
294
|
+
var rebasingBranch = (gitDir) => {
|
|
295
|
+
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
296
|
+
try {
|
|
297
|
+
const ref = fs.readFileSync(path.join(gitDir, state, "head-name"), "utf8").trim();
|
|
298
|
+
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
299
|
+
if (match)
|
|
300
|
+
return match[1];
|
|
301
|
+
} catch {}
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
};
|
|
305
|
+
function resolveTaskKey(root, location) {
|
|
306
|
+
if (!location.shared)
|
|
307
|
+
return { key: `dir-${shortHash(root)}`, kind: "dir", branch: null };
|
|
308
|
+
const branchKey = (name) => ({ key: name, kind: "branch", branch: name });
|
|
309
|
+
if (location.git && !location.git.reftable) {
|
|
310
|
+
let head = "";
|
|
311
|
+
try {
|
|
312
|
+
head = fs.readFileSync(path.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
313
|
+
} catch {}
|
|
314
|
+
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
315
|
+
if (ref && ref[1] !== ".invalid")
|
|
316
|
+
return branchKey(ref[1]);
|
|
317
|
+
if (/^[0-9a-f]{40,64}$/u.test(head)) {
|
|
318
|
+
const rebasing = rebasingBranch(location.git.gitDir);
|
|
319
|
+
if (rebasing)
|
|
320
|
+
return branchKey(rebasing);
|
|
321
|
+
return {
|
|
322
|
+
key: `detached-${shortHash(location.git.top ?? location.git.gitDir)}`,
|
|
323
|
+
kind: "detached",
|
|
324
|
+
branch: null
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const branch = gitRun(root, ["symbolic-ref", "-q", "--short", "HEAD"]);
|
|
329
|
+
const name = branch.ok ? branch.stdout.trim() : "";
|
|
330
|
+
if (name && name !== ".invalid")
|
|
331
|
+
return branchKey(name);
|
|
332
|
+
const gitDir = gitRun(root, ["rev-parse", "--path-format=absolute", "--git-dir"]);
|
|
333
|
+
if (gitDir.ok && gitDir.stdout.trim()) {
|
|
334
|
+
const rebasing = rebasingBranch(gitDir.stdout.trim());
|
|
335
|
+
if (rebasing)
|
|
336
|
+
return branchKey(rebasing);
|
|
337
|
+
}
|
|
338
|
+
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
339
|
+
const worktree = top.ok && top.stdout.trim() ? top.stdout.trim() : root;
|
|
340
|
+
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
341
|
+
}
|
|
342
|
+
function checkoutRootOf(root, location) {
|
|
343
|
+
const resolved = path.resolve(root);
|
|
344
|
+
let canonical = resolved;
|
|
345
|
+
try {
|
|
346
|
+
canonical = fs.realpathSync(resolved);
|
|
347
|
+
} catch {
|
|
348
|
+
return resolved;
|
|
349
|
+
}
|
|
350
|
+
const found = location ?? resolveStore(canonical);
|
|
351
|
+
if (found instanceof Error || !found.shared || !found.top)
|
|
352
|
+
return canonical;
|
|
353
|
+
if (real(canonical) === found.top)
|
|
354
|
+
return canonical;
|
|
355
|
+
try {
|
|
356
|
+
return fs.realpathSync(found.top);
|
|
357
|
+
} catch {
|
|
358
|
+
return canonical;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function branchRenames(location) {
|
|
362
|
+
if (!location.shared)
|
|
363
|
+
return [];
|
|
364
|
+
const files = [];
|
|
365
|
+
const walk = (dir) => {
|
|
366
|
+
let entries;
|
|
367
|
+
try {
|
|
368
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
369
|
+
} catch {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
for (const entry of entries)
|
|
373
|
+
if (entry.isDirectory())
|
|
374
|
+
walk(path.join(dir, entry.name));
|
|
375
|
+
else
|
|
376
|
+
files.push(path.join(dir, entry.name));
|
|
377
|
+
};
|
|
378
|
+
walk(path.join(path.dirname(location.dir), "logs", "refs", "heads"));
|
|
379
|
+
if (location.git)
|
|
380
|
+
files.push(path.join(location.git.gitDir, "logs", "HEAD"));
|
|
381
|
+
const seen = new Set;
|
|
382
|
+
const renames = [];
|
|
383
|
+
for (const file of files) {
|
|
384
|
+
let text = "";
|
|
385
|
+
try {
|
|
386
|
+
text = fs.readFileSync(file, "utf8");
|
|
387
|
+
} catch {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
for (const match of text.matchAll(/ (\d+) [+-]\d{4}\tBranch: renamed refs\/heads\/(\S+) to refs\/heads\/(\S+)/gu)) {
|
|
391
|
+
const rename = { from: match[2], to: match[3], at: Number(match[1]) };
|
|
392
|
+
const id = `${rename.at}\x00${rename.from}\x00${rename.to}`;
|
|
393
|
+
if (seen.has(id))
|
|
394
|
+
continue;
|
|
395
|
+
seen.add(id);
|
|
396
|
+
renames.push(rename);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return renames.toSorted((left, right) => left.at - right.at);
|
|
400
|
+
}
|
|
401
|
+
function followRenames(key, boundAt, renames) {
|
|
402
|
+
let current = key;
|
|
403
|
+
let since = Math.floor(Date.parse(boundAt) / 1000);
|
|
404
|
+
for (const rename of renames)
|
|
405
|
+
if (rename.from === current && rename.at >= since) {
|
|
406
|
+
current = rename.to;
|
|
407
|
+
since = rename.at;
|
|
408
|
+
}
|
|
409
|
+
return current;
|
|
410
|
+
}
|
|
411
|
+
var checkoutSlug = (root) => {
|
|
412
|
+
let canonical = real(root);
|
|
413
|
+
if (process.platform === "win32")
|
|
414
|
+
canonical = canonical.toLowerCase();
|
|
415
|
+
const base = path.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
|
|
416
|
+
return `${base}-${shortHash(canonical)}`;
|
|
417
|
+
};
|
|
418
|
+
|
|
137
419
|
// packages/workit-core/src/core/task-contract.ts
|
|
138
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
420
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
139
421
|
|
|
140
422
|
// node_modules/zod/v4/core/util.js
|
|
141
423
|
function getEnumValues(entries) {
|
|
@@ -8456,7 +8738,7 @@ function canonicalJson(value) {
|
|
|
8456
8738
|
function sha256(value) {
|
|
8457
8739
|
if (typeof value === "string" && invalidUnicode(value))
|
|
8458
8740
|
throw new TypeError("invalid Unicode");
|
|
8459
|
-
return
|
|
8741
|
+
return createHash2("sha256").update(typeof value === "string" ? value : canonicalJson(value), "utf8").digest("hex");
|
|
8460
8742
|
}
|
|
8461
8743
|
function newId() {
|
|
8462
8744
|
return randomUUID().toLowerCase();
|
|
@@ -8511,9 +8793,9 @@ import { spawnSync as spawnSync10 } from "node:child_process";
|
|
|
8511
8793
|
import path14 from "node:path";
|
|
8512
8794
|
|
|
8513
8795
|
// packages/workit-core/src/check-config.ts
|
|
8514
|
-
import { spawnSync } from "node:child_process";
|
|
8515
|
-
import
|
|
8516
|
-
import
|
|
8796
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
8797
|
+
import fs2 from "node:fs";
|
|
8798
|
+
import path2 from "node:path";
|
|
8517
8799
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
8518
8800
|
var CHECKS_FILE = "workit.checks.json";
|
|
8519
8801
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -8555,7 +8837,7 @@ function splitCommand(command) {
|
|
|
8555
8837
|
return argv.length ? argv : new Error("empty command");
|
|
8556
8838
|
}
|
|
8557
8839
|
var git = (cwd, args) => {
|
|
8558
|
-
const run =
|
|
8840
|
+
const run = spawnSync2("git", args, {
|
|
8559
8841
|
cwd,
|
|
8560
8842
|
encoding: "utf8",
|
|
8561
8843
|
timeout: 20000,
|
|
@@ -8566,10 +8848,10 @@ var git = (cwd, args) => {
|
|
|
8566
8848
|
return value || null;
|
|
8567
8849
|
};
|
|
8568
8850
|
function checkRoot(cwd) {
|
|
8569
|
-
return
|
|
8851
|
+
return path2.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
8570
8852
|
}
|
|
8571
8853
|
var packageManager = (root) => {
|
|
8572
|
-
const has = (file) =>
|
|
8854
|
+
const has = (file) => fs2.existsSync(path2.join(root, file));
|
|
8573
8855
|
if (has("bun.lock") || has("bun.lockb"))
|
|
8574
8856
|
return "bun";
|
|
8575
8857
|
if (has("pnpm-lock.yaml"))
|
|
@@ -8589,7 +8871,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
8589
8871
|
function detectedChecks(root) {
|
|
8590
8872
|
const read = (file) => {
|
|
8591
8873
|
try {
|
|
8592
|
-
return
|
|
8874
|
+
return fs2.readFileSync(path2.join(root, file), "utf8");
|
|
8593
8875
|
} catch {
|
|
8594
8876
|
return null;
|
|
8595
8877
|
}
|
|
@@ -8628,7 +8910,7 @@ function detectedChecks(root) {
|
|
|
8628
8910
|
var GATES = ["testing", "verification"];
|
|
8629
8911
|
function loadCheckConfig(cwd) {
|
|
8630
8912
|
const root = checkRoot(cwd);
|
|
8631
|
-
const file =
|
|
8913
|
+
const file = path2.join(root, CHECKS_FILE);
|
|
8632
8914
|
const none = (error = null) => ({
|
|
8633
8915
|
source: error ? CHECKS_FILE : "none",
|
|
8634
8916
|
root,
|
|
@@ -8638,7 +8920,7 @@ function loadCheckConfig(cwd) {
|
|
|
8638
8920
|
});
|
|
8639
8921
|
let text = null;
|
|
8640
8922
|
try {
|
|
8641
|
-
text =
|
|
8923
|
+
text = fs2.readFileSync(file, "utf8");
|
|
8642
8924
|
} catch (error) {
|
|
8643
8925
|
if (error.code !== "ENOENT")
|
|
8644
8926
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -8701,10 +8983,10 @@ function workitAvailable(env = process.env) {
|
|
|
8701
8983
|
return workitOnPath;
|
|
8702
8984
|
const windows = process.platform === "win32";
|
|
8703
8985
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
8704
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
8986
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path2.delimiter).filter(Boolean);
|
|
8705
8987
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
8706
8988
|
try {
|
|
8707
|
-
return
|
|
8989
|
+
return fs2.statSync(path2.join(dir, `workit${ext}`)).isFile();
|
|
8708
8990
|
} catch {
|
|
8709
8991
|
return false;
|
|
8710
8992
|
}
|
|
@@ -8716,15 +8998,15 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
8716
8998
|
}
|
|
8717
8999
|
|
|
8718
9000
|
// packages/workit-core/src/git/rev.ts
|
|
8719
|
-
import { spawnSync as
|
|
8720
|
-
import { createHash as
|
|
8721
|
-
import
|
|
9001
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
9002
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
9003
|
+
import fs3 from "node:fs";
|
|
8722
9004
|
import os from "node:os";
|
|
8723
|
-
import
|
|
9005
|
+
import path3 from "node:path";
|
|
8724
9006
|
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
8725
9007
|
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
8726
9008
|
var git2 = (cwd, args, options = {}) => {
|
|
8727
|
-
const result =
|
|
9009
|
+
const result = spawnSync3("git", args, {
|
|
8728
9010
|
cwd,
|
|
8729
9011
|
encoding: "utf8",
|
|
8730
9012
|
env: options.env ?? process.env,
|
|
@@ -8755,10 +9037,10 @@ function headSha(cwd) {
|
|
|
8755
9037
|
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
8756
9038
|
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
8757
9039
|
var seedIndex = (from, to) => {
|
|
8758
|
-
|
|
8759
|
-
const { atimeNs, mtimeNs } =
|
|
9040
|
+
fs3.copyFileSync(from, to);
|
|
9041
|
+
const { atimeNs, mtimeNs } = fs3.statSync(from, { bigint: true });
|
|
8760
9042
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
8761
|
-
|
|
9043
|
+
fs3.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
8762
9044
|
};
|
|
8763
9045
|
function worktreeTree(cwd, options = {}) {
|
|
8764
9046
|
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
@@ -8767,10 +9049,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
8767
9049
|
return null;
|
|
8768
9050
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
8769
9051
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
8770
|
-
const scratch =
|
|
9052
|
+
const scratch = fs3.mkdtempSync(path3.join(os.tmpdir(), "workit-tree-"));
|
|
8771
9053
|
try {
|
|
8772
|
-
const tempIndex =
|
|
8773
|
-
if (
|
|
9054
|
+
const tempIndex = path3.join(scratch, "index");
|
|
9055
|
+
if (fs3.existsSync(indexPath))
|
|
8774
9056
|
seedIndex(indexPath, tempIndex);
|
|
8775
9057
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
8776
9058
|
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
@@ -8782,7 +9064,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
8782
9064
|
const skipped = [];
|
|
8783
9065
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
8784
9066
|
try {
|
|
8785
|
-
const stat =
|
|
9067
|
+
const stat = fs3.statSync(path3.join(top, file));
|
|
8786
9068
|
if (stat.isFile() && stat.size > cap)
|
|
8787
9069
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
8788
9070
|
} catch {}
|
|
@@ -8799,10 +9081,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
8799
9081
|
if (!tree)
|
|
8800
9082
|
return null;
|
|
8801
9083
|
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
8802
|
-
const key = skipped.length ? `sha256:${
|
|
9084
|
+
const key = skipped.length ? `sha256:${createHash3("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
8803
9085
|
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
8804
9086
|
} finally {
|
|
8805
|
-
|
|
9087
|
+
fs3.rmSync(scratch, { recursive: true, force: true });
|
|
8806
9088
|
}
|
|
8807
9089
|
}
|
|
8808
9090
|
function worktreeSignal(cwd, options = {}) {
|
|
@@ -8822,16 +9104,16 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
8822
9104
|
});
|
|
8823
9105
|
if (!status.ok)
|
|
8824
9106
|
return null;
|
|
8825
|
-
const hash =
|
|
9107
|
+
const hash = createHash3("sha256");
|
|
8826
9108
|
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
8827
9109
|
for (const entry of status.stdout.split("\x00")) {
|
|
8828
9110
|
if (entry.length < 4)
|
|
8829
9111
|
continue;
|
|
8830
9112
|
const file = entry.slice(3);
|
|
8831
|
-
const absolute =
|
|
9113
|
+
const absolute = path3.join(top, file);
|
|
8832
9114
|
let stat = "-";
|
|
8833
9115
|
try {
|
|
8834
|
-
const info =
|
|
9116
|
+
const info = fs3.lstatSync(absolute, { bigint: true });
|
|
8835
9117
|
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
8836
9118
|
if (info.isDirectory())
|
|
8837
9119
|
stat += `:${headSha(absolute) ?? "-"}`;
|
|
@@ -8950,10 +9232,10 @@ function sameFileIdentity(left, right, platform = process.platform) {
|
|
|
8950
9232
|
// node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
8951
9233
|
import { randomBytes } from "node:crypto";
|
|
8952
9234
|
import fsSync from "node:fs";
|
|
8953
|
-
import
|
|
9235
|
+
import path4 from "node:path";
|
|
8954
9236
|
|
|
8955
9237
|
// node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
8956
|
-
import
|
|
9238
|
+
import fs4 from "node:fs";
|
|
8957
9239
|
|
|
8958
9240
|
// node_modules/@openclaw/fs-safe/dist/byte-budget.js
|
|
8959
9241
|
function normalizeMaxBytes(value, options = {}) {
|
|
@@ -8990,7 +9272,7 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
|
8990
9272
|
let total = 0;
|
|
8991
9273
|
while (true) {
|
|
8992
9274
|
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
8993
|
-
const bytesRead =
|
|
9275
|
+
const bytesRead = fs4.readSync(fd, scratch, 0, length, null);
|
|
8994
9276
|
if (bytesRead === 0) {
|
|
8995
9277
|
return Buffer.concat(chunks, total);
|
|
8996
9278
|
}
|
|
@@ -8999,9 +9281,9 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
|
8999
9281
|
}
|
|
9000
9282
|
|
|
9001
9283
|
// node_modules/@openclaw/fs-safe/dist/read-open-flags.js
|
|
9002
|
-
import
|
|
9284
|
+
import fs5 from "node:fs";
|
|
9003
9285
|
function resolveReadOpenFlags(options) {
|
|
9004
|
-
const constants = options?.constants ??
|
|
9286
|
+
const constants = options?.constants ?? fs5.constants;
|
|
9005
9287
|
const noFollow = process.platform !== "win32" && options?.followSymlinks !== true && typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
|
|
9006
9288
|
const nonBlocking = process.platform !== "win32" && typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0;
|
|
9007
9289
|
return constants.O_RDONLY | noFollow | nonBlocking;
|
|
@@ -9040,13 +9322,13 @@ ${ownershipToken}
|
|
|
9040
9322
|
return { raw, ownershipToken };
|
|
9041
9323
|
}
|
|
9042
9324
|
function relativeSidecarLockPath(lockRoot, lockPath) {
|
|
9043
|
-
const resolved =
|
|
9044
|
-
const lexicalRelative =
|
|
9045
|
-
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${
|
|
9046
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
9325
|
+
const resolved = path4.resolve(lockPath);
|
|
9326
|
+
const lexicalRelative = path4.relative(lockRoot.rootDir, resolved);
|
|
9327
|
+
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path4.sep}`) && !path4.isAbsolute(lexicalRelative) ? lexicalRelative : path4.relative(lockRoot.rootReal, resolved);
|
|
9328
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative)) {
|
|
9047
9329
|
throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
|
|
9048
9330
|
}
|
|
9049
|
-
return relative.split(
|
|
9331
|
+
return relative.split(path4.sep).join(path4.posix.sep);
|
|
9050
9332
|
}
|
|
9051
9333
|
function parseSidecarLockPayload(raw, parser) {
|
|
9052
9334
|
if (parser) {
|
|
@@ -9192,8 +9474,8 @@ function getFsSafeLockConfig() {
|
|
|
9192
9474
|
}
|
|
9193
9475
|
|
|
9194
9476
|
// node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
|
|
9195
|
-
import
|
|
9196
|
-
import
|
|
9477
|
+
import fs6 from "node:fs";
|
|
9478
|
+
import path5 from "node:path";
|
|
9197
9479
|
|
|
9198
9480
|
// node_modules/@openclaw/fs-safe/dist/timing.js
|
|
9199
9481
|
function sleepSync(ms) {
|
|
@@ -9223,7 +9505,7 @@ function releaseAllSyncHeldLocks() {
|
|
|
9223
9505
|
}
|
|
9224
9506
|
try {
|
|
9225
9507
|
if (held.fd !== undefined)
|
|
9226
|
-
|
|
9508
|
+
fs6.closeSync(held.fd);
|
|
9227
9509
|
} catch {}
|
|
9228
9510
|
try {
|
|
9229
9511
|
removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
|
|
@@ -9258,7 +9540,7 @@ function releaseSyncHeldLock(held) {
|
|
|
9258
9540
|
if (held.fd !== undefined) {
|
|
9259
9541
|
const fd = held.fd;
|
|
9260
9542
|
held.fd = undefined;
|
|
9261
|
-
|
|
9543
|
+
fs6.closeSync(fd);
|
|
9262
9544
|
}
|
|
9263
9545
|
removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
|
|
9264
9546
|
heldLocks.delete(held.normalizedTargetPath);
|
|
@@ -9281,29 +9563,29 @@ function createSyncHeldLockHandle(held) {
|
|
|
9281
9563
|
};
|
|
9282
9564
|
}
|
|
9283
9565
|
function canonicalLockParentSync(parent) {
|
|
9284
|
-
return process.platform === "win32" ?
|
|
9566
|
+
return process.platform === "win32" ? fs6.realpathSync.native(parent) : fs6.realpathSync(parent);
|
|
9285
9567
|
}
|
|
9286
9568
|
function normalizeTargetPath(targetPath) {
|
|
9287
|
-
const resolved =
|
|
9288
|
-
|
|
9569
|
+
const resolved = path5.resolve(targetPath);
|
|
9570
|
+
fs6.mkdirSync(path5.dirname(resolved), { recursive: true });
|
|
9289
9571
|
try {
|
|
9290
|
-
return
|
|
9572
|
+
return path5.join(canonicalLockParentSync(path5.dirname(resolved)), path5.basename(resolved));
|
|
9291
9573
|
} catch {
|
|
9292
9574
|
return resolved;
|
|
9293
9575
|
}
|
|
9294
9576
|
}
|
|
9295
9577
|
function boundedLockPath(lockPath, lockRoot) {
|
|
9296
|
-
const resolved =
|
|
9578
|
+
const resolved = path5.resolve(lockPath);
|
|
9297
9579
|
if (!lockRoot)
|
|
9298
9580
|
return resolved;
|
|
9299
9581
|
relativeSidecarLockPath(lockRoot, resolved);
|
|
9300
|
-
const parent =
|
|
9582
|
+
const parent = path5.dirname(resolved);
|
|
9301
9583
|
const parentReal = canonicalLockParentSync(parent);
|
|
9302
|
-
const parentRelative =
|
|
9303
|
-
if (parentRelative === ".." || parentRelative.startsWith(`..${
|
|
9584
|
+
const parentRelative = path5.relative(lockRoot.rootReal, parentReal);
|
|
9585
|
+
if (parentRelative === ".." || parentRelative.startsWith(`..${path5.sep}`) || path5.isAbsolute(parentRelative)) {
|
|
9304
9586
|
throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
|
|
9305
9587
|
}
|
|
9306
|
-
return
|
|
9588
|
+
return path5.join(parentReal, path5.basename(resolved));
|
|
9307
9589
|
}
|
|
9308
9590
|
function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
9309
9591
|
const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
|
|
@@ -9313,7 +9595,7 @@ function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
|
9313
9595
|
}
|
|
9314
9596
|
function reclaimGuardExists(reclaimGuardPath) {
|
|
9315
9597
|
try {
|
|
9316
|
-
|
|
9598
|
+
fs6.lstatSync(reclaimGuardPath);
|
|
9317
9599
|
return true;
|
|
9318
9600
|
} catch (error) {
|
|
9319
9601
|
if (error.code === "ENOENT")
|
|
@@ -9377,19 +9659,19 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9377
9659
|
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
9378
9660
|
let lockFileCreateDenied = false;
|
|
9379
9661
|
try {
|
|
9380
|
-
const noFollow = process.platform !== "win32" && typeof
|
|
9662
|
+
const noFollow = process.platform !== "win32" && typeof fs6.constants.O_NOFOLLOW === "number" ? fs6.constants.O_NOFOLLOW : 0;
|
|
9381
9663
|
try {
|
|
9382
|
-
fd =
|
|
9664
|
+
fd = fs6.openSync(lockPath, fs6.constants.O_WRONLY | fs6.constants.O_CREAT | fs6.constants.O_EXCL | noFollow, 384);
|
|
9383
9665
|
} catch (error) {
|
|
9384
9666
|
lockFileCreateDenied = isTransientLockFileDenial(error, lockPath);
|
|
9385
9667
|
throw error;
|
|
9386
9668
|
}
|
|
9387
|
-
|
|
9388
|
-
|
|
9669
|
+
fs6.writeFileSync(fd, raw, "utf8");
|
|
9670
|
+
fs6.fsyncSync(fd);
|
|
9389
9671
|
const snapshot = {
|
|
9390
9672
|
raw,
|
|
9391
9673
|
payload,
|
|
9392
|
-
stat:
|
|
9674
|
+
stat: fs6.fstatSync(fd),
|
|
9393
9675
|
ownershipToken
|
|
9394
9676
|
};
|
|
9395
9677
|
const createdHeld = {
|
|
@@ -9424,8 +9706,8 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9424
9706
|
return returnedHandle;
|
|
9425
9707
|
} catch (error) {
|
|
9426
9708
|
if (fd !== undefined) {
|
|
9427
|
-
const failed = { payload: null, stat:
|
|
9428
|
-
|
|
9709
|
+
const failed = { payload: null, stat: fs6.fstatSync(fd) };
|
|
9710
|
+
fs6.closeSync(fd);
|
|
9429
9711
|
fd = undefined;
|
|
9430
9712
|
removeSidecarLockIfUnchangedSync(lockPath, failed);
|
|
9431
9713
|
}
|
|
@@ -9444,393 +9726,111 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9444
9726
|
rejectNonFile: true,
|
|
9445
9727
|
onOpenFailure: (error) => {
|
|
9446
9728
|
lockFileOpenDenied = isTransientLockFileDenial(error, lockPath);
|
|
9447
|
-
}
|
|
9448
|
-
});
|
|
9449
|
-
} catch (readError) {
|
|
9450
|
-
if (lockFileOpenDenied && retryLockFileDenial(readError))
|
|
9451
|
-
continue;
|
|
9452
|
-
throw readError;
|
|
9453
|
-
}
|
|
9454
|
-
const snapshot = parseSidecarLockSnapshot(rawSnapshot, options.parsePayload);
|
|
9455
|
-
if (!snapshot) {
|
|
9456
|
-
waitForRetry();
|
|
9457
|
-
continue;
|
|
9458
|
-
}
|
|
9459
|
-
const nowMs = Date.now();
|
|
9460
|
-
const reclaim = options.shouldReclaim ? options.shouldReclaim({
|
|
9461
|
-
lockPath,
|
|
9462
|
-
normalizedTargetPath,
|
|
9463
|
-
payload: snapshot.payload,
|
|
9464
|
-
staleMs,
|
|
9465
|
-
nowMs,
|
|
9466
|
-
heldByThisProcess: false
|
|
9467
|
-
}) : defaultShouldReclaim(snapshot, staleMs, nowMs);
|
|
9468
|
-
if (reclaim) {
|
|
9469
|
-
if (staleRecovery === "remove-if-unchanged" && snapshot.raw !== undefined && options.shouldRemoveStaleLock?.({
|
|
9470
|
-
lockPath,
|
|
9471
|
-
normalizedTargetPath,
|
|
9472
|
-
raw: snapshot.raw,
|
|
9473
|
-
payload: snapshot.payload
|
|
9474
|
-
})) {
|
|
9475
|
-
let ownsReclaimGuard = false;
|
|
9476
|
-
try {
|
|
9477
|
-
fs5.mkdirSync(reclaimGuardPath);
|
|
9478
|
-
ownsReclaimGuard = true;
|
|
9479
|
-
if (removeSidecarLockIfUnchangedSync(lockPath, snapshot))
|
|
9480
|
-
continue;
|
|
9481
|
-
} catch (reclaimError) {
|
|
9482
|
-
if (reclaimError.code !== "EEXIST") {
|
|
9483
|
-
throw reclaimError;
|
|
9484
|
-
}
|
|
9485
|
-
waitForRetry();
|
|
9486
|
-
continue;
|
|
9487
|
-
} finally {
|
|
9488
|
-
if (ownsReclaimGuard) {
|
|
9489
|
-
try {
|
|
9490
|
-
fs5.rmdirSync(reclaimGuardPath);
|
|
9491
|
-
} catch {}
|
|
9492
|
-
}
|
|
9493
|
-
}
|
|
9494
|
-
}
|
|
9495
|
-
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
9496
|
-
code: "file_lock_stale",
|
|
9497
|
-
lockPath,
|
|
9498
|
-
normalizedTargetPath
|
|
9499
|
-
});
|
|
9500
|
-
}
|
|
9501
|
-
waitForRetry();
|
|
9502
|
-
}
|
|
9503
|
-
}
|
|
9504
|
-
}
|
|
9505
|
-
// packages/workit-core/src/host-session.ts
|
|
9506
|
-
var HOST_SESSION_VARS = [
|
|
9507
|
-
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9508
|
-
["PI_SESSION_ID", () => "pi"],
|
|
9509
|
-
[
|
|
9510
|
-
"CODEX_THREAD_ID",
|
|
9511
|
-
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9512
|
-
]
|
|
9513
|
-
];
|
|
9514
|
-
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9515
|
-
function hostSessionFromEnv(env) {
|
|
9516
|
-
const value = (key) => env[key]?.trim() || null;
|
|
9517
|
-
let inner = null;
|
|
9518
|
-
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9519
|
-
const session = value(key);
|
|
9520
|
-
if (session) {
|
|
9521
|
-
inner = { host: host(env), session };
|
|
9522
|
-
break;
|
|
9523
|
-
}
|
|
9524
|
-
}
|
|
9525
|
-
const declared = value("WORKIT_HOST");
|
|
9526
|
-
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9527
|
-
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9528
|
-
if (!inherited)
|
|
9529
|
-
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9530
|
-
}
|
|
9531
|
-
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9532
|
-
}
|
|
9533
|
-
|
|
9534
|
-
// packages/workit-core/src/core/package-root.ts
|
|
9535
|
-
import { existsSync } from "node:fs";
|
|
9536
|
-
import path5 from "node:path";
|
|
9537
|
-
import { fileURLToPath } from "node:url";
|
|
9538
|
-
var packageRoot = () => {
|
|
9539
|
-
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
9540
|
-
while (true) {
|
|
9541
|
-
const parent = path5.dirname(dir);
|
|
9542
|
-
if (existsSync(path5.join(dir, "package.json")) || parent === dir)
|
|
9543
|
-
return dir;
|
|
9544
|
-
dir = parent;
|
|
9545
|
-
}
|
|
9546
|
-
};
|
|
9547
|
-
|
|
9548
|
-
// packages/workit-core/src/core/store-lock.ts
|
|
9549
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
9550
|
-
import fs7 from "node:fs";
|
|
9551
|
-
import { hostname } from "node:os";
|
|
9552
|
-
|
|
9553
|
-
// packages/workit-core/src/store/paths.ts
|
|
9554
|
-
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
9555
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
9556
|
-
import fs6 from "node:fs";
|
|
9557
|
-
import path6 from "node:path";
|
|
9558
|
-
var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
|
|
9559
|
-
var gitRun = (cwd, args) => {
|
|
9560
|
-
const run = spawnSync3("git", args, {
|
|
9561
|
-
cwd,
|
|
9562
|
-
encoding: "utf8",
|
|
9563
|
-
timeout: 20000,
|
|
9564
|
-
killSignal: "SIGKILL",
|
|
9565
|
-
windowsHide: true,
|
|
9566
|
-
env: {
|
|
9567
|
-
...process.env,
|
|
9568
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
9569
|
-
GIT_OPTIONAL_LOCKS: "0",
|
|
9570
|
-
LC_ALL: "C",
|
|
9571
|
-
LANGUAGE: "C"
|
|
9572
|
-
}
|
|
9573
|
-
});
|
|
9574
|
-
const error = run.error;
|
|
9575
|
-
return {
|
|
9576
|
-
ok: run.status === 0,
|
|
9577
|
-
stdout: run.stdout ?? "",
|
|
9578
|
-
stderr: error ? String(error.message) : run.stderr ?? "",
|
|
9579
|
-
missing: error?.code === "ENOENT"
|
|
9580
|
-
};
|
|
9581
|
-
};
|
|
9582
|
-
var shortHash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 12);
|
|
9583
|
-
var real = (file) => {
|
|
9584
|
-
try {
|
|
9585
|
-
return fs6.realpathSync.native(file);
|
|
9586
|
-
} catch {
|
|
9587
|
-
return file;
|
|
9588
|
-
}
|
|
9589
|
-
};
|
|
9590
|
-
var DISCOVERY_ENV = [
|
|
9591
|
-
"GIT_DIR",
|
|
9592
|
-
"GIT_COMMON_DIR",
|
|
9593
|
-
"GIT_WORK_TREE",
|
|
9594
|
-
"GIT_CEILING_DIRECTORIES",
|
|
9595
|
-
"GIT_DISCOVERY_ACROSS_FILESYSTEM"
|
|
9596
|
-
];
|
|
9597
|
-
var isGitDir = (dir) => {
|
|
9598
|
-
try {
|
|
9599
|
-
return fs6.statSync(path6.join(dir, "HEAD")).isFile() && fs6.statSync(path6.join(dir, "objects")).isDirectory() && fs6.statSync(path6.join(dir, "refs")).isDirectory();
|
|
9600
|
-
} catch {
|
|
9601
|
-
return false;
|
|
9602
|
-
}
|
|
9603
|
-
};
|
|
9604
|
-
var reftableRefs = (common) => {
|
|
9605
|
-
try {
|
|
9606
|
-
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs6.readFileSync(path6.join(common, "config"), "utf8"));
|
|
9607
|
-
} catch {
|
|
9608
|
-
return false;
|
|
9609
|
-
}
|
|
9610
|
-
};
|
|
9611
|
-
function fastGit(root) {
|
|
9612
|
-
if (DISCOVERY_ENV.some((name) => process.env[name]))
|
|
9613
|
-
return null;
|
|
9614
|
-
let dir = real(root);
|
|
9615
|
-
let device = null;
|
|
9616
|
-
for (;; ) {
|
|
9617
|
-
let stat;
|
|
9618
|
-
try {
|
|
9619
|
-
stat = fs6.statSync(dir);
|
|
9620
|
-
} catch {
|
|
9621
|
-
return null;
|
|
9622
|
-
}
|
|
9623
|
-
if (device !== null && stat.dev !== device)
|
|
9624
|
-
return { kind: "none" };
|
|
9625
|
-
device = stat.dev;
|
|
9626
|
-
const dotGit = path6.join(dir, ".git");
|
|
9627
|
-
let entry = null;
|
|
9628
|
-
try {
|
|
9629
|
-
entry = fs6.statSync(dotGit);
|
|
9630
|
-
} catch {}
|
|
9631
|
-
let gitDir = null;
|
|
9632
|
-
let top = null;
|
|
9633
|
-
if (entry) {
|
|
9634
|
-
top = dir;
|
|
9635
|
-
if (entry.isDirectory())
|
|
9636
|
-
gitDir = dotGit;
|
|
9637
|
-
else if (entry.isFile()) {
|
|
9638
|
-
let text = "";
|
|
9639
|
-
try {
|
|
9640
|
-
text = fs6.readFileSync(dotGit, "utf8");
|
|
9641
|
-
} catch {
|
|
9642
|
-
return null;
|
|
9643
|
-
}
|
|
9644
|
-
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
9645
|
-
if (!match)
|
|
9646
|
-
return null;
|
|
9647
|
-
gitDir = path6.resolve(dir, match[1].trim());
|
|
9648
|
-
} else
|
|
9649
|
-
return null;
|
|
9650
|
-
if (!fs6.existsSync(path6.join(gitDir, "HEAD")))
|
|
9651
|
-
return null;
|
|
9652
|
-
} else if (isGitDir(dir))
|
|
9653
|
-
gitDir = dir;
|
|
9654
|
-
if (gitDir) {
|
|
9655
|
-
let common = gitDir;
|
|
9656
|
-
try {
|
|
9657
|
-
common = path6.resolve(gitDir, fs6.readFileSync(path6.join(gitDir, "commondir"), "utf8").trim());
|
|
9658
|
-
} catch {}
|
|
9659
|
-
if (!fs6.existsSync(path6.join(common, "objects")))
|
|
9660
|
-
return null;
|
|
9661
|
-
return {
|
|
9662
|
-
kind: "repo",
|
|
9663
|
-
common: real(common),
|
|
9664
|
-
gitDir: real(gitDir),
|
|
9665
|
-
top: top === null ? null : real(top),
|
|
9666
|
-
reftable: reftableRefs(common)
|
|
9667
|
-
};
|
|
9668
|
-
}
|
|
9669
|
-
const parent = path6.dirname(dir);
|
|
9670
|
-
if (parent === dir)
|
|
9671
|
-
return { kind: "none" };
|
|
9672
|
-
dir = parent;
|
|
9673
|
-
}
|
|
9674
|
-
}
|
|
9675
|
-
function resolveStore(root) {
|
|
9676
|
-
root = path6.resolve(root);
|
|
9677
|
-
let directory = false;
|
|
9678
|
-
try {
|
|
9679
|
-
directory = fs6.statSync(root).isDirectory();
|
|
9680
|
-
} catch {}
|
|
9681
|
-
if (!directory)
|
|
9682
|
-
return { dir: path6.join(root, ".workit"), shared: false, top: null };
|
|
9683
|
-
const fast = fastGit(root);
|
|
9684
|
-
if (fast?.kind === "none")
|
|
9685
|
-
return { dir: path6.join(root, ".workit"), shared: false, top: null };
|
|
9686
|
-
if (fast?.kind === "repo")
|
|
9687
|
-
return {
|
|
9688
|
-
dir: path6.join(fast.common, "workit"),
|
|
9689
|
-
shared: true,
|
|
9690
|
-
top: fast.top,
|
|
9691
|
-
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
9692
|
-
};
|
|
9693
|
-
const run = gitRun(root, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
9694
|
-
const common = run.stdout.trim();
|
|
9695
|
-
if (run.ok && common) {
|
|
9696
|
-
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
9697
|
-
return {
|
|
9698
|
-
dir: path6.join(real(path6.resolve(root, common)), "workit"),
|
|
9699
|
-
shared: true,
|
|
9700
|
-
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
9701
|
-
};
|
|
9702
|
-
}
|
|
9703
|
-
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
9704
|
-
return { dir: path6.join(root, ".workit"), shared: false, top: null };
|
|
9705
|
-
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
9706
|
-
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
9707
|
-
}
|
|
9708
|
-
var rebasingBranch = (gitDir) => {
|
|
9709
|
-
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
9710
|
-
try {
|
|
9711
|
-
const ref = fs6.readFileSync(path6.join(gitDir, state, "head-name"), "utf8").trim();
|
|
9712
|
-
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
9713
|
-
if (match)
|
|
9714
|
-
return match[1];
|
|
9715
|
-
} catch {}
|
|
9716
|
-
}
|
|
9717
|
-
return null;
|
|
9718
|
-
};
|
|
9719
|
-
function resolveTaskKey(root, location) {
|
|
9720
|
-
if (!location.shared)
|
|
9721
|
-
return { key: `dir-${shortHash(root)}`, kind: "dir", branch: null };
|
|
9722
|
-
const branchKey = (name) => ({ key: name, kind: "branch", branch: name });
|
|
9723
|
-
if (location.git && !location.git.reftable) {
|
|
9724
|
-
let head = "";
|
|
9725
|
-
try {
|
|
9726
|
-
head = fs6.readFileSync(path6.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
9727
|
-
} catch {}
|
|
9728
|
-
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
9729
|
-
if (ref && ref[1] !== ".invalid")
|
|
9730
|
-
return branchKey(ref[1]);
|
|
9731
|
-
if (/^[0-9a-f]{40,64}$/u.test(head)) {
|
|
9732
|
-
const rebasing = rebasingBranch(location.git.gitDir);
|
|
9733
|
-
if (rebasing)
|
|
9734
|
-
return branchKey(rebasing);
|
|
9735
|
-
return {
|
|
9736
|
-
key: `detached-${shortHash(location.git.top ?? location.git.gitDir)}`,
|
|
9737
|
-
kind: "detached",
|
|
9738
|
-
branch: null
|
|
9739
|
-
};
|
|
9740
|
-
}
|
|
9741
|
-
}
|
|
9742
|
-
const branch = gitRun(root, ["symbolic-ref", "-q", "--short", "HEAD"]);
|
|
9743
|
-
const name = branch.ok ? branch.stdout.trim() : "";
|
|
9744
|
-
if (name && name !== ".invalid")
|
|
9745
|
-
return branchKey(name);
|
|
9746
|
-
const gitDir = gitRun(root, ["rev-parse", "--path-format=absolute", "--git-dir"]);
|
|
9747
|
-
if (gitDir.ok && gitDir.stdout.trim()) {
|
|
9748
|
-
const rebasing = rebasingBranch(gitDir.stdout.trim());
|
|
9749
|
-
if (rebasing)
|
|
9750
|
-
return branchKey(rebasing);
|
|
9751
|
-
}
|
|
9752
|
-
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
9753
|
-
const worktree = top.ok && top.stdout.trim() ? top.stdout.trim() : root;
|
|
9754
|
-
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
9755
|
-
}
|
|
9756
|
-
function checkoutRootOf(root, location) {
|
|
9757
|
-
const resolved = path6.resolve(root);
|
|
9758
|
-
let canonical = resolved;
|
|
9759
|
-
try {
|
|
9760
|
-
canonical = fs6.realpathSync(resolved);
|
|
9761
|
-
} catch {
|
|
9762
|
-
return resolved;
|
|
9763
|
-
}
|
|
9764
|
-
const found = location ?? resolveStore(canonical);
|
|
9765
|
-
if (found instanceof Error || !found.shared || !found.top)
|
|
9766
|
-
return canonical;
|
|
9767
|
-
if (real(canonical) === found.top)
|
|
9768
|
-
return canonical;
|
|
9769
|
-
try {
|
|
9770
|
-
return fs6.realpathSync(found.top);
|
|
9771
|
-
} catch {
|
|
9772
|
-
return canonical;
|
|
9773
|
-
}
|
|
9774
|
-
}
|
|
9775
|
-
function branchRenames(location) {
|
|
9776
|
-
if (!location.shared)
|
|
9777
|
-
return [];
|
|
9778
|
-
const files = [];
|
|
9779
|
-
const walk = (dir) => {
|
|
9780
|
-
let entries;
|
|
9781
|
-
try {
|
|
9782
|
-
entries = fs6.readdirSync(dir, { withFileTypes: true });
|
|
9783
|
-
} catch {
|
|
9784
|
-
return;
|
|
9785
|
-
}
|
|
9786
|
-
for (const entry of entries)
|
|
9787
|
-
if (entry.isDirectory())
|
|
9788
|
-
walk(path6.join(dir, entry.name));
|
|
9789
|
-
else
|
|
9790
|
-
files.push(path6.join(dir, entry.name));
|
|
9791
|
-
};
|
|
9792
|
-
walk(path6.join(path6.dirname(location.dir), "logs", "refs", "heads"));
|
|
9793
|
-
if (location.git)
|
|
9794
|
-
files.push(path6.join(location.git.gitDir, "logs", "HEAD"));
|
|
9795
|
-
const seen = new Set;
|
|
9796
|
-
const renames = [];
|
|
9797
|
-
for (const file of files) {
|
|
9798
|
-
let text = "";
|
|
9799
|
-
try {
|
|
9800
|
-
text = fs6.readFileSync(file, "utf8");
|
|
9801
|
-
} catch {
|
|
9802
|
-
continue;
|
|
9803
|
-
}
|
|
9804
|
-
for (const match of text.matchAll(/ (\d+) [+-]\d{4}\tBranch: renamed refs\/heads\/(\S+) to refs\/heads\/(\S+)/gu)) {
|
|
9805
|
-
const rename = { from: match[2], to: match[3], at: Number(match[1]) };
|
|
9806
|
-
const id = `${rename.at}\x00${rename.from}\x00${rename.to}`;
|
|
9807
|
-
if (seen.has(id))
|
|
9729
|
+
}
|
|
9730
|
+
});
|
|
9731
|
+
} catch (readError) {
|
|
9732
|
+
if (lockFileOpenDenied && retryLockFileDenial(readError))
|
|
9733
|
+
continue;
|
|
9734
|
+
throw readError;
|
|
9735
|
+
}
|
|
9736
|
+
const snapshot = parseSidecarLockSnapshot(rawSnapshot, options.parsePayload);
|
|
9737
|
+
if (!snapshot) {
|
|
9738
|
+
waitForRetry();
|
|
9808
9739
|
continue;
|
|
9809
|
-
|
|
9810
|
-
|
|
9740
|
+
}
|
|
9741
|
+
const nowMs = Date.now();
|
|
9742
|
+
const reclaim = options.shouldReclaim ? options.shouldReclaim({
|
|
9743
|
+
lockPath,
|
|
9744
|
+
normalizedTargetPath,
|
|
9745
|
+
payload: snapshot.payload,
|
|
9746
|
+
staleMs,
|
|
9747
|
+
nowMs,
|
|
9748
|
+
heldByThisProcess: false
|
|
9749
|
+
}) : defaultShouldReclaim(snapshot, staleMs, nowMs);
|
|
9750
|
+
if (reclaim) {
|
|
9751
|
+
if (staleRecovery === "remove-if-unchanged" && snapshot.raw !== undefined && options.shouldRemoveStaleLock?.({
|
|
9752
|
+
lockPath,
|
|
9753
|
+
normalizedTargetPath,
|
|
9754
|
+
raw: snapshot.raw,
|
|
9755
|
+
payload: snapshot.payload
|
|
9756
|
+
})) {
|
|
9757
|
+
let ownsReclaimGuard = false;
|
|
9758
|
+
try {
|
|
9759
|
+
fs6.mkdirSync(reclaimGuardPath);
|
|
9760
|
+
ownsReclaimGuard = true;
|
|
9761
|
+
if (removeSidecarLockIfUnchangedSync(lockPath, snapshot))
|
|
9762
|
+
continue;
|
|
9763
|
+
} catch (reclaimError) {
|
|
9764
|
+
if (reclaimError.code !== "EEXIST") {
|
|
9765
|
+
throw reclaimError;
|
|
9766
|
+
}
|
|
9767
|
+
waitForRetry();
|
|
9768
|
+
continue;
|
|
9769
|
+
} finally {
|
|
9770
|
+
if (ownsReclaimGuard) {
|
|
9771
|
+
try {
|
|
9772
|
+
fs6.rmdirSync(reclaimGuardPath);
|
|
9773
|
+
} catch {}
|
|
9774
|
+
}
|
|
9775
|
+
}
|
|
9776
|
+
}
|
|
9777
|
+
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
9778
|
+
code: "file_lock_stale",
|
|
9779
|
+
lockPath,
|
|
9780
|
+
normalizedTargetPath
|
|
9781
|
+
});
|
|
9782
|
+
}
|
|
9783
|
+
waitForRetry();
|
|
9811
9784
|
}
|
|
9812
9785
|
}
|
|
9813
|
-
return renames.toSorted((left, right) => left.at - right.at);
|
|
9814
9786
|
}
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9787
|
+
// packages/workit-core/src/host-session.ts
|
|
9788
|
+
var HOST_SESSION_VARS = [
|
|
9789
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9790
|
+
["PI_SESSION_ID", () => "pi"],
|
|
9791
|
+
[
|
|
9792
|
+
"CODEX_THREAD_ID",
|
|
9793
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9794
|
+
]
|
|
9795
|
+
];
|
|
9796
|
+
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9797
|
+
function hostSessionFromEnv(env) {
|
|
9798
|
+
const value = (key) => env[key]?.trim() || null;
|
|
9799
|
+
let inner = null;
|
|
9800
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9801
|
+
const session = value(key);
|
|
9802
|
+
if (session) {
|
|
9803
|
+
inner = { host: host(env), session };
|
|
9804
|
+
break;
|
|
9822
9805
|
}
|
|
9823
|
-
|
|
9806
|
+
}
|
|
9807
|
+
const declared = value("WORKIT_HOST");
|
|
9808
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9809
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9810
|
+
if (!inherited)
|
|
9811
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9812
|
+
}
|
|
9813
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9824
9814
|
}
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9815
|
+
|
|
9816
|
+
// packages/workit-core/src/core/package-root.ts
|
|
9817
|
+
import { existsSync } from "node:fs";
|
|
9818
|
+
import path6 from "node:path";
|
|
9819
|
+
import { fileURLToPath } from "node:url";
|
|
9820
|
+
var packageRoot = () => {
|
|
9821
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
9822
|
+
while (true) {
|
|
9823
|
+
const parent = path6.dirname(dir);
|
|
9824
|
+
if (existsSync(path6.join(dir, "package.json")) || parent === dir)
|
|
9825
|
+
return dir;
|
|
9826
|
+
dir = parent;
|
|
9827
|
+
}
|
|
9831
9828
|
};
|
|
9832
9829
|
|
|
9833
9830
|
// packages/workit-core/src/core/store-lock.ts
|
|
9831
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
9832
|
+
import fs7 from "node:fs";
|
|
9833
|
+
import { hostname } from "node:os";
|
|
9834
9834
|
var metadataLockSchema = object({
|
|
9835
9835
|
pid: number2().int().nonnegative().safe(),
|
|
9836
9836
|
processStart: string2().nullable(),
|
|
@@ -17422,8 +17422,77 @@ var turnContextText = (input, descriptor) => {
|
|
|
17422
17422
|
return null;
|
|
17423
17423
|
}
|
|
17424
17424
|
};
|
|
17425
|
+
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
17426
|
+
var digestOf = (text) => createHash10("sha256").update(text).digest("hex");
|
|
17427
|
+
var turnMarker = (input) => {
|
|
17428
|
+
if (!input.session.id)
|
|
17429
|
+
return null;
|
|
17430
|
+
try {
|
|
17431
|
+
const location = resolveStore(input.cwd);
|
|
17432
|
+
if (location instanceof Error)
|
|
17433
|
+
return null;
|
|
17434
|
+
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
17435
|
+
return path16.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
17436
|
+
} catch {
|
|
17437
|
+
return null;
|
|
17438
|
+
}
|
|
17439
|
+
};
|
|
17440
|
+
var writeTurnDigest = (file, digest) => {
|
|
17441
|
+
try {
|
|
17442
|
+
fs13.mkdirSync(path16.dirname(file), { recursive: true });
|
|
17443
|
+
fs13.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
17444
|
+
`);
|
|
17445
|
+
} catch {}
|
|
17446
|
+
};
|
|
17447
|
+
var clearTurnDigest = (file) => {
|
|
17448
|
+
try {
|
|
17449
|
+
fs13.rmSync(file, { force: true });
|
|
17450
|
+
} catch {}
|
|
17451
|
+
};
|
|
17452
|
+
var pruneTurnDigests = (dir, now) => {
|
|
17453
|
+
try {
|
|
17454
|
+
for (const name of fs13.readdirSync(dir)) {
|
|
17455
|
+
if (!name.startsWith("turn-"))
|
|
17456
|
+
continue;
|
|
17457
|
+
const file = path16.join(dir, name);
|
|
17458
|
+
try {
|
|
17459
|
+
if (now - fs13.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
17460
|
+
fs13.rmSync(file, { force: true });
|
|
17461
|
+
} catch {}
|
|
17462
|
+
}
|
|
17463
|
+
} catch {}
|
|
17464
|
+
};
|
|
17465
|
+
var changedTurnContext = (input, text) => {
|
|
17466
|
+
const file = turnMarker(input);
|
|
17467
|
+
if (!text) {
|
|
17468
|
+
if (file)
|
|
17469
|
+
clearTurnDigest(file);
|
|
17470
|
+
return null;
|
|
17471
|
+
}
|
|
17472
|
+
if (!file)
|
|
17473
|
+
return text;
|
|
17474
|
+
const digest = digestOf(text);
|
|
17475
|
+
try {
|
|
17476
|
+
const previous = JSON.parse(fs13.readFileSync(file, "utf8"));
|
|
17477
|
+
if (previous.digest === digest)
|
|
17478
|
+
return null;
|
|
17479
|
+
} catch {}
|
|
17480
|
+
writeTurnDigest(file, digest);
|
|
17481
|
+
return text;
|
|
17482
|
+
};
|
|
17483
|
+
var seedTurnContext = (input, descriptor) => {
|
|
17484
|
+
const file = turnMarker(input);
|
|
17485
|
+
if (!file)
|
|
17486
|
+
return;
|
|
17487
|
+
pruneTurnDigests(path16.dirname(file), Date.now());
|
|
17488
|
+
const text = turnContextText(input, descriptor);
|
|
17489
|
+
if (text)
|
|
17490
|
+
writeTurnDigest(file, digestOf(text));
|
|
17491
|
+
else
|
|
17492
|
+
clearTurnDigest(file);
|
|
17493
|
+
};
|
|
17425
17494
|
// packages/workit-core/src/core/branch.ts
|
|
17426
|
-
import { createHash as
|
|
17495
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
17427
17496
|
var withReleaseTracks = (workspaceRoot, policy) => {
|
|
17428
17497
|
const read = workspaceReleaseTracks(workspaceRoot);
|
|
17429
17498
|
if (read.error)
|
|
@@ -17491,7 +17560,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
17491
17560
|
const source = resolved.provenance.branchPolicy;
|
|
17492
17561
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
17493
17562
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
17494
|
-
const fingerprint =
|
|
17563
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
17495
17564
|
return {
|
|
17496
17565
|
branchPolicy,
|
|
17497
17566
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -17826,7 +17895,7 @@ function writeGate(cwd, paths) {
|
|
|
17826
17895
|
const gate = gateFor(cwd);
|
|
17827
17896
|
if (!gate || gate.blockers.length === 0)
|
|
17828
17897
|
return NONE2;
|
|
17829
|
-
if (paths && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
17898
|
+
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
17830
17899
|
return NONE2;
|
|
17831
17900
|
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
17832
17901
|
return {
|
|
@@ -17840,8 +17909,8 @@ function writeGate(cwd, paths) {
|
|
|
17840
17909
|
}
|
|
17841
17910
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
17842
17911
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17843
|
-
import { createHash as
|
|
17844
|
-
import
|
|
17912
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
17913
|
+
import fs14 from "node:fs";
|
|
17845
17914
|
import os4 from "node:os";
|
|
17846
17915
|
import path18 from "node:path";
|
|
17847
17916
|
var NONE3 = { kind: "none" };
|
|
@@ -18087,7 +18156,7 @@ var repoAt = (dir) => {
|
|
|
18087
18156
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
18088
18157
|
};
|
|
18089
18158
|
var isWorkitWorkspace = (repo) => {
|
|
18090
|
-
if (
|
|
18159
|
+
if (fs14.existsSync(repo.location.dir))
|
|
18091
18160
|
return true;
|
|
18092
18161
|
try {
|
|
18093
18162
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -18118,7 +18187,7 @@ var headSha2 = (repo) => {
|
|
|
18118
18187
|
if (!git || git.reftable)
|
|
18119
18188
|
return null;
|
|
18120
18189
|
try {
|
|
18121
|
-
const head =
|
|
18190
|
+
const head = fs14.readFileSync(path18.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18122
18191
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18123
18192
|
return head;
|
|
18124
18193
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
@@ -18126,9 +18195,9 @@ var headSha2 = (repo) => {
|
|
|
18126
18195
|
return null;
|
|
18127
18196
|
const common = path18.dirname(repo.location.dir);
|
|
18128
18197
|
try {
|
|
18129
|
-
return
|
|
18198
|
+
return fs14.readFileSync(path18.join(common, ref), "utf8").trim() || null;
|
|
18130
18199
|
} catch {}
|
|
18131
|
-
const packed =
|
|
18200
|
+
const packed = fs14.readFileSync(path18.join(common, "packed-refs"), "utf8");
|
|
18132
18201
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18133
18202
|
} catch {
|
|
18134
18203
|
return null;
|
|
@@ -18176,6 +18245,11 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
18176
18245
|
}
|
|
18177
18246
|
return out;
|
|
18178
18247
|
};
|
|
18248
|
+
var inWorkitWorkspace = (dir) => {
|
|
18249
|
+
const repo = repoAt(dir);
|
|
18250
|
+
return repo !== null && isWorkitWorkspace(repo);
|
|
18251
|
+
};
|
|
18252
|
+
var rawDelivery = (cwd, command, dialect) => mentionsRawTool(command) && actionsIn(cwd, command, dialect).some(({ action }) => action.kind !== "commit" && action.kind !== "pr-read");
|
|
18179
18253
|
var mentionsRawTool = (command) => /\b(?:git|gh|glab)\b/i.test(command);
|
|
18180
18254
|
var dialectOf = (input) => (input.event.kind === "shell.pre" || input.event.kind === "shell.post" ? input.event.dialect : undefined) ?? "posix";
|
|
18181
18255
|
function rawGitPre(input, command) {
|
|
@@ -18205,8 +18279,8 @@ function rawGitPre(input, command) {
|
|
|
18205
18279
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
18206
18280
|
var NEXT_COMMAND = "next";
|
|
18207
18281
|
var MARKER_TTL_SECONDS = 3600;
|
|
18208
|
-
var markerFile = (repo, session, key) => path18.join(repo.location.dir, "hooks", `commit-${
|
|
18209
|
-
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${
|
|
18282
|
+
var markerFile = (repo, session, key) => path18.join(repo.location.dir, "hooks", `commit-${createHash12("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18283
|
+
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash12("sha256").update(command).digest("hex")}`;
|
|
18210
18284
|
function noteRawCommit(input, command, key) {
|
|
18211
18285
|
try {
|
|
18212
18286
|
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
@@ -18216,8 +18290,8 @@ function noteRawCommit(input, command, key) {
|
|
|
18216
18290
|
if (action.kind !== "commit")
|
|
18217
18291
|
continue;
|
|
18218
18292
|
const file = markerFile(repo, input.session.id, key);
|
|
18219
|
-
|
|
18220
|
-
|
|
18293
|
+
fs14.mkdirSync(path18.dirname(file), { recursive: true });
|
|
18294
|
+
fs14.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18221
18295
|
`);
|
|
18222
18296
|
}
|
|
18223
18297
|
} catch {}
|
|
@@ -18240,11 +18314,11 @@ var headCommit = (dir) => {
|
|
|
18240
18314
|
var settle2 = (input, repo, file) => {
|
|
18241
18315
|
let marker;
|
|
18242
18316
|
try {
|
|
18243
|
-
marker = JSON.parse(
|
|
18317
|
+
marker = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
18244
18318
|
} catch {
|
|
18245
18319
|
return;
|
|
18246
18320
|
}
|
|
18247
|
-
|
|
18321
|
+
fs14.rmSync(file, { force: true });
|
|
18248
18322
|
const session = input.session.id;
|
|
18249
18323
|
const now = Date.now() / 1000;
|
|
18250
18324
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -18293,18 +18367,292 @@ function settlePendingCommit(input) {
|
|
|
18293
18367
|
if (!repo)
|
|
18294
18368
|
return;
|
|
18295
18369
|
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
18296
|
-
if (
|
|
18370
|
+
if (fs14.existsSync(file))
|
|
18297
18371
|
settle2(input, repo, file);
|
|
18298
18372
|
} catch {}
|
|
18299
18373
|
}
|
|
18374
|
+
// packages/workit-core/src/hooks/skill-nudge.ts
|
|
18375
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
18376
|
+
import fs15 from "node:fs";
|
|
18377
|
+
import path19 from "node:path";
|
|
18378
|
+
|
|
18379
|
+
// packages/workit-core/src/core/skill-manifests.ts
|
|
18380
|
+
var WORKIT_METHOD_SKILLS = [
|
|
18381
|
+
"workit-shape",
|
|
18382
|
+
"workit-implement",
|
|
18383
|
+
"workit-review",
|
|
18384
|
+
"workit-debug",
|
|
18385
|
+
"workit-ship",
|
|
18386
|
+
"workit-continue",
|
|
18387
|
+
"workit-bdd",
|
|
18388
|
+
"workit-test-audit",
|
|
18389
|
+
"workit-deslop",
|
|
18390
|
+
"workit-fanout",
|
|
18391
|
+
"workit-verify-app",
|
|
18392
|
+
"workit-retro",
|
|
18393
|
+
"workit-architecture"
|
|
18394
|
+
];
|
|
18395
|
+
var WORKIT_SKILL_ALIASES = {
|
|
18396
|
+
"wk-shape": "workit-shape",
|
|
18397
|
+
"wk-implement": "workit-implement",
|
|
18398
|
+
"wk-review": "workit-review",
|
|
18399
|
+
"wk-debug": "workit-debug",
|
|
18400
|
+
"wk-ship": "workit-ship",
|
|
18401
|
+
"wk-continue": "workit-continue",
|
|
18402
|
+
"wk-bdd": "workit-bdd",
|
|
18403
|
+
"wk-test-audit": "workit-test-audit",
|
|
18404
|
+
"wk-deslop": "workit-deslop",
|
|
18405
|
+
"wk-fanout": "workit-fanout",
|
|
18406
|
+
"wk-verify-app": "workit-verify-app",
|
|
18407
|
+
"wk-retro": "workit-retro",
|
|
18408
|
+
"wk-architecture": "workit-architecture"
|
|
18409
|
+
};
|
|
18410
|
+
var skillLoadWording = (host, skill) => {
|
|
18411
|
+
switch (host) {
|
|
18412
|
+
case "claude-code":
|
|
18413
|
+
return `call the Skill tool with \`workit:${skill.replace(/^workit-/, "")}\``;
|
|
18414
|
+
case "opencode":
|
|
18415
|
+
return `call the skill tool with \`${skill}\``;
|
|
18416
|
+
default:
|
|
18417
|
+
return `read the \`${skill}\` skill's SKILL.md and follow it`;
|
|
18418
|
+
}
|
|
18419
|
+
};
|
|
18420
|
+
if (false) {}
|
|
18421
|
+
|
|
18422
|
+
// packages/workit-core/src/hooks/skill-nudge.ts
|
|
18423
|
+
var USER_INVOKED = new Set(["workit-retro", "workit-architecture"]);
|
|
18424
|
+
var SKILL_HOST = {
|
|
18425
|
+
claude_code: "claude-code",
|
|
18426
|
+
opencode: "opencode",
|
|
18427
|
+
codex_cli: "codex",
|
|
18428
|
+
codex_desktop: "codex",
|
|
18429
|
+
cursor: "cursor",
|
|
18430
|
+
pi: "pi"
|
|
18431
|
+
};
|
|
18432
|
+
var PROMPT_INTENTS = [
|
|
18433
|
+
[
|
|
18434
|
+
"workit-debug",
|
|
18435
|
+
/\b(flaky|regression|(?:is|are|got|looks) broken|debug (?:this|it|the|why)|fix (?:the|this|a) bug)\b/i
|
|
18436
|
+
],
|
|
18437
|
+
[
|
|
18438
|
+
"workit-continue",
|
|
18439
|
+
/\b(pick up where|where (?:were|did) we|resume (?:the|this|my|our|work|where)|hand ?off)\b/i
|
|
18440
|
+
],
|
|
18441
|
+
[
|
|
18442
|
+
"workit-review",
|
|
18443
|
+
/\b(blast radius|code review|review (?:the|this|my|PR|pr|#\d+|branch|diff|changes))\b/i
|
|
18444
|
+
],
|
|
18445
|
+
[
|
|
18446
|
+
"workit-ship",
|
|
18447
|
+
/\b(ship (?:it|this|the)|babysit|open (?:a|the) (?:PR|pr|MR|mr)|merge (?:the|this|my) (?:PR|pr|MR|mr|branch)|(?:get|until|make) CI (?:is )?green)\b/i
|
|
18448
|
+
],
|
|
18449
|
+
["workit-bdd", /\b(BDD|TDD|acceptance criteria|Given\/When\/Then)(?![\w/])/],
|
|
18450
|
+
["workit-test-audit", /\b(test audit|audit the tests|tautolog\w*|weak tests)\b/i],
|
|
18451
|
+
["workit-deslop", /\b(deslop|slop|dead code)\b/i],
|
|
18452
|
+
["workit-fanout", /\b(fan(?: |-)?out|parallelize|parallel agents|swarm)\b/i],
|
|
18453
|
+
["workit-verify-app", /\b(verify the app|smoke test|prove it works)\b/i],
|
|
18454
|
+
["workit-retro", /\b(retro|retrospective)\b(?!\.\w)/i],
|
|
18455
|
+
["workit-architecture", /\b(deepen (?:the )?modules|architecture (?:review|pass|audit))\b/i],
|
|
18456
|
+
[
|
|
18457
|
+
"workit-shape",
|
|
18458
|
+
/\b(brainstorm\w*|grill me|should we|let'?s plan|plan (?:out|how|the|a|for)|(?:write|draft) (?:a|the) spec)\b/i
|
|
18459
|
+
],
|
|
18460
|
+
[
|
|
18461
|
+
"workit-implement",
|
|
18462
|
+
/\b(implement (?:the|this|a|it)|add (?:a|the) feature|build (?:a|an|the) (?:new )?(?:feature|command|verb|page|endpoint|component|cli|tool))\b/i
|
|
18463
|
+
]
|
|
18464
|
+
];
|
|
18465
|
+
var workitSkillOf = (name) => {
|
|
18466
|
+
const bare = name.trim().replace(/^\//, "").toLowerCase();
|
|
18467
|
+
const alias = WORKIT_SKILL_ALIASES[bare];
|
|
18468
|
+
if (alias)
|
|
18469
|
+
return alias;
|
|
18470
|
+
const skill = bare.replace(/^workit:/, "workit-");
|
|
18471
|
+
return WORKIT_METHOD_SKILLS.includes(skill) ? skill : null;
|
|
18472
|
+
};
|
|
18473
|
+
var promptTrigger = (prompt) => {
|
|
18474
|
+
const text = prompt.replace(/```[\s\S]*?```/g, " ").slice(0, 4000);
|
|
18475
|
+
for (const [skill, pattern] of PROMPT_INTENTS) {
|
|
18476
|
+
const match = pattern.exec(text);
|
|
18477
|
+
if (match)
|
|
18478
|
+
return { skill, trigger: match[1] ?? match[0] };
|
|
18479
|
+
}
|
|
18480
|
+
return null;
|
|
18481
|
+
};
|
|
18482
|
+
var markerFile2 = (input, session) => {
|
|
18483
|
+
const location = resolveStore(input.cwd);
|
|
18484
|
+
if (location instanceof Error || !location.shared)
|
|
18485
|
+
return null;
|
|
18486
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18487
|
+
return path19.join(location.dir, "hooks", `skills-${name}.json`);
|
|
18488
|
+
};
|
|
18489
|
+
var readMarker = (file) => {
|
|
18490
|
+
try {
|
|
18491
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
18492
|
+
return { nudged: value.nudged ?? [], loaded: value.loaded ?? [] };
|
|
18493
|
+
} catch {
|
|
18494
|
+
return { nudged: [], loaded: [] };
|
|
18495
|
+
}
|
|
18496
|
+
};
|
|
18497
|
+
var writeMarker = (file, marker) => {
|
|
18498
|
+
try {
|
|
18499
|
+
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18500
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
18501
|
+
} catch {}
|
|
18502
|
+
};
|
|
18503
|
+
var loadedSkills = (cwd, session) => {
|
|
18504
|
+
const ledger = readLedger(cwd);
|
|
18505
|
+
const loaded = new Set;
|
|
18506
|
+
if (!ledger.ok)
|
|
18507
|
+
return loaded;
|
|
18508
|
+
for (const row of ledger.value.rows)
|
|
18509
|
+
if (row.type === "skill.loaded" && row.actor.session === session && typeof row.skill === "string")
|
|
18510
|
+
loaded.add(row.skill);
|
|
18511
|
+
return loaded;
|
|
18512
|
+
};
|
|
18513
|
+
var SKILL_FILE = /(?:^|[\\/])(workit-[a-z-]+)[\\/]SKILL\.md$/;
|
|
18514
|
+
var skillFileIn = (file) => SKILL_FILE.exec(file)?.[1] ?? null;
|
|
18515
|
+
var READERS = new Set([
|
|
18516
|
+
"cat",
|
|
18517
|
+
"sed",
|
|
18518
|
+
"head",
|
|
18519
|
+
"tail",
|
|
18520
|
+
"less",
|
|
18521
|
+
"more",
|
|
18522
|
+
"bat",
|
|
18523
|
+
"nl",
|
|
18524
|
+
"get-content",
|
|
18525
|
+
"gc",
|
|
18526
|
+
"type"
|
|
18527
|
+
]);
|
|
18528
|
+
var skillReadIn = (command, dialect = "posix") => {
|
|
18529
|
+
for (const { words } of segmentsOf(command, dialect)) {
|
|
18530
|
+
if (!READERS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
|
|
18531
|
+
continue;
|
|
18532
|
+
for (const word of words.slice(1)) {
|
|
18533
|
+
const skill = skillFileIn(word);
|
|
18534
|
+
if (skill)
|
|
18535
|
+
return skill;
|
|
18536
|
+
}
|
|
18537
|
+
}
|
|
18538
|
+
return null;
|
|
18539
|
+
};
|
|
18540
|
+
function recordSkillLoad(input, name, via) {
|
|
18541
|
+
try {
|
|
18542
|
+
const skill = workitSkillOf(name);
|
|
18543
|
+
const session = input.session.id;
|
|
18544
|
+
if (!skill || !session)
|
|
18545
|
+
return;
|
|
18546
|
+
if (!inWorkitWorkspace(input.cwd))
|
|
18547
|
+
return;
|
|
18548
|
+
const file = markerFile2(input, session);
|
|
18549
|
+
if (!file)
|
|
18550
|
+
return;
|
|
18551
|
+
const marker = readMarker(file);
|
|
18552
|
+
if (marker.loaded.includes(skill))
|
|
18553
|
+
return;
|
|
18554
|
+
writeMarker(file, { ...marker, loaded: [...marker.loaded, skill] });
|
|
18555
|
+
if (loadedSkills(input.cwd, session).has(skill))
|
|
18556
|
+
return;
|
|
18557
|
+
appendHookObserved(input.cwd, {
|
|
18558
|
+
type: "skill.loaded",
|
|
18559
|
+
actor: { host: input.host, session, agentId: input.session.agentId },
|
|
18560
|
+
skill,
|
|
18561
|
+
via
|
|
18562
|
+
});
|
|
18563
|
+
} catch {}
|
|
18564
|
+
}
|
|
18565
|
+
var nudgeOnce = (input, session, skill, line) => {
|
|
18566
|
+
const file = markerFile2(input, session);
|
|
18567
|
+
if (!file)
|
|
18568
|
+
return null;
|
|
18569
|
+
const marker = readMarker(file);
|
|
18570
|
+
if (marker.nudged.includes(skill) || marker.loaded.includes(skill))
|
|
18571
|
+
return null;
|
|
18572
|
+
if (loadedSkills(input.cwd, session).has(skill))
|
|
18573
|
+
return null;
|
|
18574
|
+
writeMarker(file, { ...marker, nudged: [...marker.nudged, skill] });
|
|
18575
|
+
return line;
|
|
18576
|
+
};
|
|
18577
|
+
var nudgeText = (input, skill, why) => USER_INVOKED.has(skill) ? `Workit: ${why} looks like ${skill} work, which the user starts; offer it (/${skill.replace(/^workit-/, "wk-")}) rather than loading it.` : `Workit: ${why} looks like ${skill} work; if it is, ${skillLoadWording(SKILL_HOST[input.host], skill)} first.`;
|
|
18578
|
+
var promptLoads = (prompt) => /^\s*\/(?:skill:)?([\w:-]+)/.exec(prompt)?.[1] ?? /^\s*<skill name="([\w:-]+)"/.exec(prompt)?.[1] ?? /^\s*Load the (workit-[a-z-]+) skill with the skill tool\b/.exec(prompt)?.[1] ?? null;
|
|
18579
|
+
function promptNudge(input, prompt) {
|
|
18580
|
+
try {
|
|
18581
|
+
const session = input.session.id;
|
|
18582
|
+
if (!prompt || !session || input.session.agentId)
|
|
18583
|
+
return null;
|
|
18584
|
+
const loads = promptLoads(prompt);
|
|
18585
|
+
if (loads) {
|
|
18586
|
+
recordSkillLoad(input, loads, "slash");
|
|
18587
|
+
return null;
|
|
18588
|
+
}
|
|
18589
|
+
const hit = promptTrigger(prompt);
|
|
18590
|
+
if (!hit || !inWorkitWorkspace(input.cwd))
|
|
18591
|
+
return null;
|
|
18592
|
+
return nudgeOnce(input, session, hit.skill, nudgeText(input, hit.skill, `this request ("${hit.trigger}")`));
|
|
18593
|
+
} catch {
|
|
18594
|
+
return null;
|
|
18595
|
+
}
|
|
18596
|
+
}
|
|
18597
|
+
var WORKIT_DELIVERY = /^(?:pr (?:create|merge)|ci wait|git push)\b/;
|
|
18598
|
+
var SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
18599
|
+
var LAUNCHERS = new Set([
|
|
18600
|
+
"npx",
|
|
18601
|
+
"bunx",
|
|
18602
|
+
"pnpx",
|
|
18603
|
+
"-y",
|
|
18604
|
+
"--yes",
|
|
18605
|
+
"pnpm",
|
|
18606
|
+
"dlx",
|
|
18607
|
+
"exec",
|
|
18608
|
+
"env",
|
|
18609
|
+
"sudo",
|
|
18610
|
+
"command"
|
|
18611
|
+
]);
|
|
18612
|
+
var runsWorkitDelivery = (command, dialect, depth = 0) => segmentsOf(command, dialect).some(({ words }) => {
|
|
18613
|
+
const at = words.findIndex((word) => !/^\w+=/.test(word) && !LAUNCHERS.has(word));
|
|
18614
|
+
if (at >= 0 && /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/.test(words[at])) {
|
|
18615
|
+
const args = words.slice(at + 1);
|
|
18616
|
+
if (args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run"))
|
|
18617
|
+
return false;
|
|
18618
|
+
return WORKIT_DELIVERY.test(args.join(" "));
|
|
18619
|
+
}
|
|
18620
|
+
const shell = SHELLS2.has(words[at]?.split(/[\\/]/).at(-1) ?? "");
|
|
18621
|
+
const script = shell && words[at + 1] === "-c" ? words[at + 2] : undefined;
|
|
18622
|
+
return depth < 3 && script !== undefined && runsWorkitDelivery(script, "posix", depth + 1);
|
|
18623
|
+
});
|
|
18624
|
+
var isDeliveryCommand = (cwd, command, dialect = "posix") => rawDelivery(cwd, command, dialect) || runsWorkitDelivery(command, dialect) && inWorkitWorkspace(cwd);
|
|
18625
|
+
function shipNudge(input, command) {
|
|
18626
|
+
try {
|
|
18627
|
+
const session = input.session.id;
|
|
18628
|
+
if (!session || input.session.agentId)
|
|
18629
|
+
return null;
|
|
18630
|
+
const dialect = input.event.kind === "shell.pre" ? input.event.dialect ?? "posix" : "posix";
|
|
18631
|
+
if (!isDeliveryCommand(input.cwd, command, dialect))
|
|
18632
|
+
return null;
|
|
18633
|
+
return nudgeOnce(input, session, "workit-ship", nudgeText(input, "workit-ship", "this delivery step"));
|
|
18634
|
+
} catch {
|
|
18635
|
+
return null;
|
|
18636
|
+
}
|
|
18637
|
+
}
|
|
18638
|
+
var withContextLine = (decision, line) => {
|
|
18639
|
+
if (!line)
|
|
18640
|
+
return decision;
|
|
18641
|
+
if (decision.kind === "none")
|
|
18642
|
+
return { kind: "context", text: line };
|
|
18643
|
+
if (decision.kind === "context")
|
|
18644
|
+
return { kind: "context", text: `${decision.text}
|
|
18645
|
+
${line}` };
|
|
18646
|
+
return decision;
|
|
18647
|
+
};
|
|
18300
18648
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18301
18649
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
18302
|
-
import
|
|
18650
|
+
import path20 from "node:path";
|
|
18303
18651
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18304
18652
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
18305
18653
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
18306
18654
|
var existingDirectory = (value) => {
|
|
18307
|
-
if (!nonEmpty2(value) || !
|
|
18655
|
+
if (!nonEmpty2(value) || !path20.isAbsolute(value) || !existsSync6(value))
|
|
18308
18656
|
return null;
|
|
18309
18657
|
try {
|
|
18310
18658
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -18312,7 +18660,7 @@ var existingDirectory = (value) => {
|
|
|
18312
18660
|
return null;
|
|
18313
18661
|
}
|
|
18314
18662
|
};
|
|
18315
|
-
var
|
|
18663
|
+
var SHELLS3 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
18316
18664
|
var SCRIPT_FLAG2 = /^(?:-\w*c|-command|\/c)$/i;
|
|
18317
18665
|
var quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg);
|
|
18318
18666
|
var commandText = (value) => {
|
|
@@ -18324,7 +18672,7 @@ var commandText = (value) => {
|
|
|
18324
18672
|
if (!argv.every((arg) => typeof arg === "string"))
|
|
18325
18673
|
return null;
|
|
18326
18674
|
const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
|
|
18327
|
-
if (
|
|
18675
|
+
if (SHELLS3.has(shell))
|
|
18328
18676
|
for (let index = 1;index < argv.length - 1; index++) {
|
|
18329
18677
|
if (SCRIPT_FLAG2.test(argv[index]))
|
|
18330
18678
|
return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
|
|
@@ -18355,30 +18703,49 @@ var WRITE_TOOLS = new Set([
|
|
|
18355
18703
|
]);
|
|
18356
18704
|
var isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, ""));
|
|
18357
18705
|
var PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
|
|
18706
|
+
var PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
|
|
18358
18707
|
var writePaths = (input) => {
|
|
18359
18708
|
if (!isRecord4(input))
|
|
18360
18709
|
return [];
|
|
18361
18710
|
const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
|
|
18362
18711
|
for (const value of Object.values(input))
|
|
18363
18712
|
if (typeof value === "string")
|
|
18364
|
-
for (const match of value.matchAll(
|
|
18713
|
+
for (const match of value.matchAll(PATCH_HEADER))
|
|
18365
18714
|
paths.push(match[1].trim());
|
|
18366
18715
|
return [...new Set(paths)];
|
|
18367
18716
|
};
|
|
18717
|
+
var writeTargets = (input) => {
|
|
18718
|
+
const paths = writePaths(input);
|
|
18719
|
+
return paths.length > 0 ? paths : null;
|
|
18720
|
+
};
|
|
18368
18721
|
// packages/workit-core/src/hooks/handle.ts
|
|
18369
18722
|
var usable2 = (value) => value === "native" || value === "partial";
|
|
18370
18723
|
var NONE4 = { kind: "none" };
|
|
18724
|
+
var resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn);
|
|
18371
18725
|
var offered = new Set;
|
|
18372
|
-
var
|
|
18373
|
-
|
|
18726
|
+
var ROLES = {
|
|
18727
|
+
implementer: "implementer",
|
|
18728
|
+
verifier: "judge",
|
|
18729
|
+
reviewer: "judge"
|
|
18730
|
+
};
|
|
18731
|
+
var workitRole = (descriptor, agentType) => {
|
|
18732
|
+
const prefix = descriptor.subagents.agentPrefix;
|
|
18733
|
+
const name = agentType.startsWith(prefix) ? agentType.slice(prefix.length) : "";
|
|
18734
|
+
return Object.hasOwn(ROLES, name) ? ROLES[name] : null;
|
|
18735
|
+
};
|
|
18374
18736
|
var subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128);
|
|
18375
|
-
var subagentStartText = (
|
|
18376
|
-
|
|
18377
|
-
|
|
18378
|
-
|
|
18379
|
-
|
|
18380
|
-
return
|
|
18381
|
-
|
|
18737
|
+
var subagentStartText = (session, descriptor, event) => {
|
|
18738
|
+
const role = workitRole(descriptor, event.agentType);
|
|
18739
|
+
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
18740
|
+
if (role === "implementer") {
|
|
18741
|
+
const where = descriptor.subagents.worktreeIsolation === "native" ? "working in its own git worktree: it may edit and commit there" : "as an implementer: it edits and commits only in its own git worktree (the lead makes one with `workit fanout worktree create <slice>`), never in the lead's checkout";
|
|
18742
|
+
return `Workit observed ${who} ${where}, 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.`;
|
|
18743
|
+
}
|
|
18744
|
+
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
18745
|
+
if (role !== "judge" || !event.agentId)
|
|
18746
|
+
return readOnly;
|
|
18747
|
+
const own = subagentSession(session, event.agentId);
|
|
18748
|
+
return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result> --session ${own} ...\` so the ledger tells it apart from the author.`;
|
|
18382
18749
|
};
|
|
18383
18750
|
function handleHook(input, deps) {
|
|
18384
18751
|
const { descriptor } = deps;
|
|
@@ -18394,18 +18761,22 @@ function handleHook(input, deps) {
|
|
|
18394
18761
|
const advisory = usable2(descriptor.events["write.pre"].support) ? null : `Workit cannot gate file writes on ${descriptor.label} (no pre-write hook): before-write requirements (an open product choice, a missing plan) are advisory here, so settle them before editing.`;
|
|
18395
18762
|
const addendum = [deps.addendum, advisory].filter(Boolean).join(`
|
|
18396
18763
|
`) || null;
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
};
|
|
18764
|
+
const text = sessionContextText(input, descriptor, { offer, addendum });
|
|
18765
|
+
if (resendsOnChange(descriptor))
|
|
18766
|
+
seedTurnContext(input, descriptor);
|
|
18767
|
+
return { kind: "context", text };
|
|
18401
18768
|
}
|
|
18402
18769
|
case "context.turn": {
|
|
18403
18770
|
if (!usable2(descriptor.context.perTurn))
|
|
18404
18771
|
return NONE4;
|
|
18405
|
-
const
|
|
18406
|
-
|
|
18772
|
+
const current = turnContextText(input, descriptor);
|
|
18773
|
+
const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
|
|
18774
|
+
return withContextLine(text ? { kind: "context", text } : NONE4, promptNudge(input, event.prompt));
|
|
18407
18775
|
}
|
|
18408
18776
|
case "shell.pre": {
|
|
18777
|
+
const skillRead = skillReadIn(event.command, event.dialect);
|
|
18778
|
+
if (skillRead)
|
|
18779
|
+
recordSkillLoad(input, skillRead, "read");
|
|
18409
18780
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
18410
18781
|
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
|
|
18411
18782
|
if (policy.kind !== "none")
|
|
@@ -18421,7 +18792,7 @@ function handleHook(input, deps) {
|
|
|
18421
18792
|
if (gate.kind === "deny")
|
|
18422
18793
|
return gate;
|
|
18423
18794
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
18424
|
-
return raw;
|
|
18795
|
+
return withContextLine(raw, shipNudge(input, event.command));
|
|
18425
18796
|
}
|
|
18426
18797
|
case "shell.post":
|
|
18427
18798
|
if (usable2(descriptor.events["shell.post"].support))
|
|
@@ -18432,7 +18803,7 @@ function handleHook(input, deps) {
|
|
|
18432
18803
|
case "subagent.start":
|
|
18433
18804
|
return {
|
|
18434
18805
|
kind: "context",
|
|
18435
|
-
text: subagentStartText(input.
|
|
18806
|
+
text: subagentStartText(input.session.id ?? null, descriptor, event)
|
|
18436
18807
|
};
|
|
18437
18808
|
case "compact.pre":
|
|
18438
18809
|
return {
|
|
@@ -18440,6 +18811,9 @@ function handleHook(input, deps) {
|
|
|
18440
18811
|
userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
|
|
18441
18812
|
};
|
|
18442
18813
|
case "tool.pre":
|
|
18814
|
+
if (event.skill)
|
|
18815
|
+
recordSkillLoad(input, event.skill, "tool");
|
|
18816
|
+
return NONE4;
|
|
18443
18817
|
case "subagent.stop":
|
|
18444
18818
|
case "prompt.submit":
|
|
18445
18819
|
case "stop":
|
|
@@ -18490,24 +18864,26 @@ var CLAUDE_CODE_DESCRIPTOR = {
|
|
|
18490
18864
|
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18491
18865
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18492
18866
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18493
|
-
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18494
18867
|
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18495
|
-
"
|
|
18496
|
-
|
|
18868
|
+
"subagent.stop": { support: "none", native: null },
|
|
18869
|
+
"compact.pre": { support: "none", native: null },
|
|
18870
|
+
stop: { support: "none", native: null }
|
|
18497
18871
|
},
|
|
18498
18872
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18499
18873
|
context: {
|
|
18500
18874
|
sessionStart: "native",
|
|
18501
18875
|
perTurn: "native",
|
|
18502
18876
|
afterCompact: "native",
|
|
18503
|
-
task: "session-bound"
|
|
18877
|
+
task: "session-bound",
|
|
18878
|
+
turnResend: "every-turn"
|
|
18504
18879
|
},
|
|
18505
18880
|
subagents: {
|
|
18506
18881
|
identity: "native",
|
|
18507
18882
|
parentBinding: "partial",
|
|
18508
18883
|
blockStart: "none",
|
|
18509
18884
|
worktreeIsolation: "native",
|
|
18510
|
-
maxConcurrency: "undocumented"
|
|
18885
|
+
maxConcurrency: "undocumented",
|
|
18886
|
+
agentPrefix: "workit:"
|
|
18511
18887
|
},
|
|
18512
18888
|
provenance: { sessionId: "native", agentIdOnTool: "native", postToolObserve: "native" },
|
|
18513
18889
|
interaction: { questions: "undocumented", writeBoundary: "partial" },
|
|
@@ -18525,13 +18901,13 @@ var CLAUDE_CODE_DESCRIPTOR = {
|
|
|
18525
18901
|
},
|
|
18526
18902
|
{
|
|
18527
18903
|
name: "native_subagents",
|
|
18528
|
-
surface: "SubagentStart
|
|
18529
|
-
refs: ["SubagentStart"
|
|
18530
|
-
requires: ["event:subagent.start"
|
|
18531
|
-
observed: ["subagent.start"
|
|
18904
|
+
surface: "SubagentStart",
|
|
18905
|
+
refs: ["SubagentStart"],
|
|
18906
|
+
requires: ["event:subagent.start"],
|
|
18907
|
+
observed: ["subagent.start"],
|
|
18532
18908
|
assurance: "agent_guided",
|
|
18533
18909
|
reason: "Claude Code reports stable agent identities, but SubagentStart cannot block or bind",
|
|
18534
|
-
unavailableReason: "Claude Code
|
|
18910
|
+
unavailableReason: "Claude Code SubagentStart is unavailable"
|
|
18535
18911
|
},
|
|
18536
18912
|
{
|
|
18537
18913
|
name: "fresh-context-review",
|
|
@@ -18582,7 +18958,7 @@ var eventOf = (name, value) => {
|
|
|
18582
18958
|
case "SessionStart":
|
|
18583
18959
|
return SOURCES.has(value.source) ? { ok: true, event: { kind: "session.start", source: value.source } } : { ok: false, error: "SessionStart source is required" };
|
|
18584
18960
|
case "UserPromptSubmit":
|
|
18585
|
-
return { ok: true, event: { kind: "context.turn" } };
|
|
18961
|
+
return { ok: true, event: { kind: "context.turn", prompt: optionalText(value.prompt) } };
|
|
18586
18962
|
case "PreToolUse": {
|
|
18587
18963
|
if (!nonEmpty2(value.tool_name))
|
|
18588
18964
|
return { ok: false, error: "tool_name is required" };
|
|
@@ -18592,10 +18968,20 @@ var eventOf = (name, value) => {
|
|
|
18592
18968
|
event: {
|
|
18593
18969
|
kind: "write.pre",
|
|
18594
18970
|
tool: value.tool_name,
|
|
18595
|
-
paths:
|
|
18971
|
+
paths: writeTargets(value.tool_input),
|
|
18596
18972
|
toolUseId
|
|
18597
18973
|
}
|
|
18598
18974
|
};
|
|
18975
|
+
if (value.tool_name === "Skill")
|
|
18976
|
+
return {
|
|
18977
|
+
ok: true,
|
|
18978
|
+
event: {
|
|
18979
|
+
kind: "tool.pre",
|
|
18980
|
+
tool: value.tool_name,
|
|
18981
|
+
toolUseId,
|
|
18982
|
+
skill: isRecord4(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
18983
|
+
}
|
|
18984
|
+
};
|
|
18599
18985
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
18600
18986
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
18601
18987
|
const command = toolCommand(value);
|
|
@@ -18730,39 +19116,40 @@ var claudeCodeAdapter = {
|
|
|
18730
19116
|
function detectCodexSurface(env) {
|
|
18731
19117
|
return env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || Boolean(env.CODEX_ELECTRON_RESOURCES_PATH) ? "codex_desktop" : "codex_cli";
|
|
18732
19118
|
}
|
|
18733
|
-
var undocumented = { support: "undocumented", native: null };
|
|
18734
19119
|
var CODEX_DESCRIPTOR = {
|
|
18735
19120
|
host: "codex_cli",
|
|
18736
19121
|
label: "Codex",
|
|
18737
|
-
verifiedAgainst: "codex-cli 0.
|
|
19122
|
+
verifiedAgainst: "codex-cli 0.160.1 (hook JSON schemas in the binary); Codex Desktop 26.901.20858",
|
|
18738
19123
|
docs: ["https://developers.openai.com/codex/hooks"],
|
|
18739
19124
|
transport: "hook-process",
|
|
18740
19125
|
events: {
|
|
18741
19126
|
"session.start": { support: "native", native: "SessionStart" },
|
|
18742
|
-
"context.turn":
|
|
19127
|
+
"context.turn": { support: "native", native: "UserPromptSubmit" },
|
|
18743
19128
|
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
18744
19129
|
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
18745
|
-
"write.pre": { support: "
|
|
19130
|
+
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18746
19131
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18747
19132
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18748
19133
|
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18749
|
-
"prompt.submit":
|
|
19134
|
+
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18750
19135
|
"compact.pre": { support: "none", native: null },
|
|
18751
|
-
stop:
|
|
19136
|
+
stop: { support: "none", native: null }
|
|
18752
19137
|
},
|
|
18753
19138
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18754
19139
|
context: {
|
|
18755
19140
|
sessionStart: "native",
|
|
18756
|
-
perTurn: "
|
|
19141
|
+
perTurn: "native",
|
|
18757
19142
|
afterCompact: "native",
|
|
18758
|
-
task: "single-active"
|
|
19143
|
+
task: "single-active",
|
|
19144
|
+
turnResend: "on-change"
|
|
18759
19145
|
},
|
|
18760
19146
|
subagents: {
|
|
18761
19147
|
identity: "native",
|
|
18762
19148
|
parentBinding: "none",
|
|
18763
19149
|
blockStart: "none",
|
|
18764
19150
|
worktreeIsolation: "undocumented",
|
|
18765
|
-
maxConcurrency: "undocumented"
|
|
19151
|
+
maxConcurrency: "undocumented",
|
|
19152
|
+
agentPrefix: "workit-"
|
|
18766
19153
|
},
|
|
18767
19154
|
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "native" },
|
|
18768
19155
|
interaction: { questions: "none", writeBoundary: "partial" },
|
|
@@ -18829,6 +19216,7 @@ var CODEX_DESCRIPTOR = {
|
|
|
18829
19216
|
};
|
|
18830
19217
|
var EVENTS2 = {
|
|
18831
19218
|
SessionStart: "session.start",
|
|
19219
|
+
UserPromptSubmit: "context.turn",
|
|
18832
19220
|
PreToolUse: "shell.pre",
|
|
18833
19221
|
PostToolUse: "shell.post",
|
|
18834
19222
|
SubagentStart: "subagent.start",
|
|
@@ -18836,7 +19224,7 @@ var EVENTS2 = {
|
|
|
18836
19224
|
};
|
|
18837
19225
|
var SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
|
|
18838
19226
|
var isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase());
|
|
18839
|
-
var SOURCES2 = new Set(["startup", "resume", "clear", "compact"]);
|
|
19227
|
+
var SOURCES2 = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
18840
19228
|
var parseCodexHookInput = (value) => {
|
|
18841
19229
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
|
|
18842
19230
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -18863,6 +19251,7 @@ var parseCodexHookInput = (value) => {
|
|
|
18863
19251
|
cwd,
|
|
18864
19252
|
...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
|
|
18865
19253
|
...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
|
|
19254
|
+
...event === "UserPromptSubmit" ? { prompt: optionalText(value.prompt) } : {},
|
|
18866
19255
|
...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
|
|
18867
19256
|
...toolEvent ? { tool_input: value.tool_input } : {},
|
|
18868
19257
|
...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
|
|
@@ -18882,13 +19271,20 @@ var protocolEvent = (input) => {
|
|
|
18882
19271
|
switch (input.hook_event_name) {
|
|
18883
19272
|
case "SessionStart":
|
|
18884
19273
|
return { kind: "session.start", source: input.source };
|
|
19274
|
+
case "UserPromptSubmit":
|
|
19275
|
+
return { kind: "context.turn", prompt: input.prompt ?? null };
|
|
18885
19276
|
case "PreToolUse": {
|
|
18886
19277
|
const toolUseId = input.tool_use_id ?? null;
|
|
18887
|
-
|
|
18888
|
-
kind: "shell.pre",
|
|
18889
|
-
|
|
18890
|
-
|
|
18891
|
-
|
|
19278
|
+
if (isShellTool(input.tool_name))
|
|
19279
|
+
return { kind: "shell.pre", command: input.command ?? "", toolUseId };
|
|
19280
|
+
if (isWriteTool(input.tool_name))
|
|
19281
|
+
return {
|
|
19282
|
+
kind: "write.pre",
|
|
19283
|
+
tool: input.tool_name,
|
|
19284
|
+
paths: writeTargets(input.tool_input),
|
|
19285
|
+
toolUseId
|
|
19286
|
+
};
|
|
19287
|
+
return { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
18892
19288
|
}
|
|
18893
19289
|
case "PostToolUse": {
|
|
18894
19290
|
const toolUseId = input.tool_use_id ?? null;
|
|
@@ -18964,12 +19360,12 @@ var codexAdapter = {
|
|
|
18964
19360
|
};
|
|
18965
19361
|
},
|
|
18966
19362
|
render: render2,
|
|
18967
|
-
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>)
|
|
19363
|
+
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 its own session, never the author's: a workit-verifier or workit-reviewer agent uses the --session its SubagentStart context names; any other uses one the lead assigns (WORKIT_HOST=codex_cli WORKIT_SESSION_ID=<lead>-v<n>). Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
|
|
18968
19364
|
};
|
|
18969
19365
|
|
|
18970
19366
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
18971
|
-
import
|
|
18972
|
-
var
|
|
19367
|
+
import path21 from "node:path";
|
|
19368
|
+
var undocumented = { support: "undocumented", native: null };
|
|
18973
19369
|
var none = { support: "none", native: null };
|
|
18974
19370
|
var CURSOR_DESCRIPTOR = {
|
|
18975
19371
|
host: "cursor",
|
|
@@ -18983,26 +19379,28 @@ var CURSOR_DESCRIPTOR = {
|
|
|
18983
19379
|
"shell.pre": { support: "native", native: "beforeShellExecution" },
|
|
18984
19380
|
"tool.pre": { support: "native", native: "preToolUse" },
|
|
18985
19381
|
"write.pre": { support: "partial", native: "preToolUse" },
|
|
18986
|
-
"shell.post":
|
|
19382
|
+
"shell.post": undocumented,
|
|
18987
19383
|
"subagent.start": { support: "native", native: "subagentStart" },
|
|
18988
19384
|
"subagent.stop": { support: "partial", native: "subagentStop" },
|
|
18989
|
-
"prompt.submit":
|
|
19385
|
+
"prompt.submit": undocumented,
|
|
18990
19386
|
"compact.pre": { support: "partial", native: "preCompact" },
|
|
18991
|
-
stop:
|
|
19387
|
+
stop: none
|
|
18992
19388
|
},
|
|
18993
19389
|
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: true },
|
|
18994
19390
|
context: {
|
|
18995
19391
|
sessionStart: "native",
|
|
18996
19392
|
perTurn: "none",
|
|
18997
19393
|
afterCompact: "partial",
|
|
18998
|
-
task: "single-active"
|
|
19394
|
+
task: "single-active",
|
|
19395
|
+
turnResend: "every-turn"
|
|
18999
19396
|
},
|
|
19000
19397
|
subagents: {
|
|
19001
19398
|
identity: "native",
|
|
19002
19399
|
parentBinding: "native",
|
|
19003
19400
|
blockStart: "native",
|
|
19004
19401
|
worktreeIsolation: "undocumented",
|
|
19005
|
-
maxConcurrency: "undocumented"
|
|
19402
|
+
maxConcurrency: "undocumented",
|
|
19403
|
+
agentPrefix: "workit-"
|
|
19006
19404
|
},
|
|
19007
19405
|
provenance: {
|
|
19008
19406
|
sessionId: "native",
|
|
@@ -19127,7 +19525,7 @@ var protocolEvent2 = (input) => {
|
|
|
19127
19525
|
return isWriteTool(input.tool_name) ? {
|
|
19128
19526
|
kind: "write.pre",
|
|
19129
19527
|
tool: input.tool_name ?? "",
|
|
19130
|
-
paths:
|
|
19528
|
+
paths: writeTargets(input.tool_input),
|
|
19131
19529
|
toolUseId: null
|
|
19132
19530
|
} : { kind: "tool.pre", tool: input.tool_name ?? "", toolUseId: null };
|
|
19133
19531
|
case "subagentStart":
|
|
@@ -19149,7 +19547,7 @@ var protocolEvent2 = (input) => {
|
|
|
19149
19547
|
return { kind: "compact.pre", trigger: "auto" };
|
|
19150
19548
|
}
|
|
19151
19549
|
};
|
|
19152
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
19550
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path21.sep) ? root : `${root}${path21.sep}`);
|
|
19153
19551
|
var hookCwd = (input) => {
|
|
19154
19552
|
const cwd = existingDirectory(input.cwd);
|
|
19155
19553
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|