@brainervirus/workit-pi 8.1.0 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/worker.js +3 -0
- package/dist/workit.js +870 -105
- package/package.json +1 -1
- package/skills/workit-ship/SKILL.md +14 -14
package/dist/workit.js
CHANGED
|
@@ -3,7 +3,7 @@ import { randomUUID as randomUUID5 } from "node:crypto";
|
|
|
3
3
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4
4
|
|
|
5
5
|
// packages/workit-core/src/core.ts
|
|
6
|
-
import { spawnSync as
|
|
6
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
7
7
|
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
8
8
|
import path19 from "node:path";
|
|
9
9
|
|
|
@@ -9596,6 +9596,35 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9596
9596
|
}
|
|
9597
9597
|
}
|
|
9598
9598
|
}
|
|
9599
|
+
// packages/workit-core/src/host-session.ts
|
|
9600
|
+
var HOST_SESSION_VARS = [
|
|
9601
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9602
|
+
["PI_SESSION_ID", () => "pi"],
|
|
9603
|
+
[
|
|
9604
|
+
"CODEX_THREAD_ID",
|
|
9605
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9606
|
+
]
|
|
9607
|
+
];
|
|
9608
|
+
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9609
|
+
function hostSessionFromEnv(env) {
|
|
9610
|
+
const value = (key) => env[key]?.trim() || null;
|
|
9611
|
+
let inner = null;
|
|
9612
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9613
|
+
const session = value(key);
|
|
9614
|
+
if (session) {
|
|
9615
|
+
inner = { host: host(env), session };
|
|
9616
|
+
break;
|
|
9617
|
+
}
|
|
9618
|
+
}
|
|
9619
|
+
const declared = value("WORKIT_HOST");
|
|
9620
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9621
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9622
|
+
if (!inherited)
|
|
9623
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9624
|
+
}
|
|
9625
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9626
|
+
}
|
|
9627
|
+
|
|
9599
9628
|
// packages/workit-core/src/core/package-root.ts
|
|
9600
9629
|
import { existsSync } from "node:fs";
|
|
9601
9630
|
import path3 from "node:path";
|
|
@@ -10560,9 +10589,10 @@ var drop = (lock) => {
|
|
|
10560
10589
|
};
|
|
10561
10590
|
var envActor = () => {
|
|
10562
10591
|
const value = (key) => process.env[key]?.trim() || null;
|
|
10592
|
+
const acting = hostSessionFromEnv(process.env);
|
|
10563
10593
|
return {
|
|
10564
|
-
host:
|
|
10565
|
-
session:
|
|
10594
|
+
host: acting.host ?? "runtime",
|
|
10595
|
+
session: acting.session,
|
|
10566
10596
|
agentId: value("WORKIT_AGENT_ID")
|
|
10567
10597
|
};
|
|
10568
10598
|
};
|
|
@@ -12486,6 +12516,7 @@ import { createHash as createHash6, randomBytes as randomBytes2 } from "node:cry
|
|
|
12486
12516
|
import fs10 from "node:fs";
|
|
12487
12517
|
import path9 from "node:path";
|
|
12488
12518
|
var LEDGER_VERSION = 1;
|
|
12519
|
+
var MAX_LINE_BYTES = 4096;
|
|
12489
12520
|
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
12490
12521
|
var FAILING = new Set(["failed", "blocked"]);
|
|
12491
12522
|
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
@@ -12585,6 +12616,7 @@ function ledgerPath(cwd) {
|
|
|
12585
12616
|
const root = storeRoot(cwd);
|
|
12586
12617
|
return root.ok ? { ok: true, value: path9.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
12587
12618
|
}
|
|
12619
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
|
|
12588
12620
|
var NETWORK_FS_TYPES = new Set([
|
|
12589
12621
|
26985,
|
|
12590
12622
|
20859,
|
|
@@ -12597,6 +12629,158 @@ var NETWORK_FS_TYPES = new Set([
|
|
|
12597
12629
|
1196443219,
|
|
12598
12630
|
198183888
|
|
12599
12631
|
]);
|
|
12632
|
+
var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
|
|
12633
|
+
var onNetworkFs = (dir) => {
|
|
12634
|
+
if (process.platform !== "linux")
|
|
12635
|
+
return false;
|
|
12636
|
+
try {
|
|
12637
|
+
return isNetworkFsType(fs10.statfsSync(dir).type);
|
|
12638
|
+
} catch {
|
|
12639
|
+
return false;
|
|
12640
|
+
}
|
|
12641
|
+
};
|
|
12642
|
+
var LOCK_WAIT_MS = 5000;
|
|
12643
|
+
var LOCK_STALE_MS = 30000;
|
|
12644
|
+
var LOCK_OWNER = "owner";
|
|
12645
|
+
var lockToken = (lock) => {
|
|
12646
|
+
try {
|
|
12647
|
+
return fs10.readFileSync(path9.join(lock, LOCK_OWNER), "utf8");
|
|
12648
|
+
} catch {
|
|
12649
|
+
return null;
|
|
12650
|
+
}
|
|
12651
|
+
};
|
|
12652
|
+
var lockAgeMs = (lock) => {
|
|
12653
|
+
for (const target of [path9.join(lock, LOCK_OWNER), lock]) {
|
|
12654
|
+
try {
|
|
12655
|
+
return Date.now() - fs10.statSync(target).mtimeMs;
|
|
12656
|
+
} catch {}
|
|
12657
|
+
}
|
|
12658
|
+
return null;
|
|
12659
|
+
};
|
|
12660
|
+
var ledgerLock = {
|
|
12661
|
+
acquire(lock) {
|
|
12662
|
+
try {
|
|
12663
|
+
fs10.mkdirSync(lock);
|
|
12664
|
+
} catch (error) {
|
|
12665
|
+
if (error.code === "EEXIST")
|
|
12666
|
+
return null;
|
|
12667
|
+
throw error;
|
|
12668
|
+
}
|
|
12669
|
+
const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
|
|
12670
|
+
fs10.writeFileSync(path9.join(lock, LOCK_OWNER), token);
|
|
12671
|
+
return token;
|
|
12672
|
+
},
|
|
12673
|
+
release(lock, token) {
|
|
12674
|
+
if (lockToken(lock) !== token)
|
|
12675
|
+
return false;
|
|
12676
|
+
const tomb = `${lock}.release-${randomBytes2(6).toString("hex")}`;
|
|
12677
|
+
try {
|
|
12678
|
+
fs10.renameSync(lock, tomb);
|
|
12679
|
+
} catch {
|
|
12680
|
+
return false;
|
|
12681
|
+
}
|
|
12682
|
+
if (lockToken(tomb) !== token) {
|
|
12683
|
+
try {
|
|
12684
|
+
fs10.renameSync(tomb, lock);
|
|
12685
|
+
} catch {}
|
|
12686
|
+
return false;
|
|
12687
|
+
}
|
|
12688
|
+
fs10.rmSync(tomb, { recursive: true, force: true });
|
|
12689
|
+
return true;
|
|
12690
|
+
},
|
|
12691
|
+
token: lockToken,
|
|
12692
|
+
reap(lock, observed) {
|
|
12693
|
+
const tomb = `${lock}.reap-${randomBytes2(6).toString("hex")}`;
|
|
12694
|
+
try {
|
|
12695
|
+
fs10.renameSync(lock, tomb);
|
|
12696
|
+
} catch {
|
|
12697
|
+
return false;
|
|
12698
|
+
}
|
|
12699
|
+
if (lockToken(tomb) === observed) {
|
|
12700
|
+
fs10.rmSync(tomb, { recursive: true, force: true });
|
|
12701
|
+
return true;
|
|
12702
|
+
}
|
|
12703
|
+
try {
|
|
12704
|
+
fs10.renameSync(tomb, lock);
|
|
12705
|
+
} catch {
|
|
12706
|
+
fs10.rmSync(tomb, { recursive: true, force: true });
|
|
12707
|
+
}
|
|
12708
|
+
return false;
|
|
12709
|
+
}
|
|
12710
|
+
};
|
|
12711
|
+
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
12712
|
+
const lock = `${file}.lock`;
|
|
12713
|
+
const deadline = Date.now() + waitMs;
|
|
12714
|
+
let token = null;
|
|
12715
|
+
for (;; ) {
|
|
12716
|
+
token = ledgerLock.acquire(lock);
|
|
12717
|
+
if (token)
|
|
12718
|
+
break;
|
|
12719
|
+
const observed = lockToken(lock);
|
|
12720
|
+
const age = lockAgeMs(lock);
|
|
12721
|
+
if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
|
|
12722
|
+
continue;
|
|
12723
|
+
if (Date.now() >= deadline)
|
|
12724
|
+
return null;
|
|
12725
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
12726
|
+
}
|
|
12727
|
+
try {
|
|
12728
|
+
return fn();
|
|
12729
|
+
} finally {
|
|
12730
|
+
ledgerLock.release(lock, token);
|
|
12731
|
+
}
|
|
12732
|
+
}
|
|
12733
|
+
function appendLine(file, line, options) {
|
|
12734
|
+
const write = () => {
|
|
12735
|
+
const fd = fs10.openSync(file, "a+");
|
|
12736
|
+
try {
|
|
12737
|
+
const size = fs10.fstatSync(fd).size;
|
|
12738
|
+
let fence = "";
|
|
12739
|
+
if (size > 0) {
|
|
12740
|
+
const last = Buffer.alloc(1);
|
|
12741
|
+
fs10.readSync(fd, last, 0, 1, size - 1);
|
|
12742
|
+
if (last[0] !== 10)
|
|
12743
|
+
fence = `
|
|
12744
|
+
`;
|
|
12745
|
+
}
|
|
12746
|
+
const bytes = Buffer.from(fence + line);
|
|
12747
|
+
const written = fs10.writeSync(fd, bytes);
|
|
12748
|
+
if (written !== bytes.length)
|
|
12749
|
+
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
12750
|
+
return { ok: true, value: undefined };
|
|
12751
|
+
} finally {
|
|
12752
|
+
fs10.closeSync(fd);
|
|
12753
|
+
}
|
|
12754
|
+
};
|
|
12755
|
+
try {
|
|
12756
|
+
fs10.mkdirSync(path9.dirname(file), { recursive: true });
|
|
12757
|
+
if (!options.forceLock && !onNetworkFs(path9.dirname(file)))
|
|
12758
|
+
return write();
|
|
12759
|
+
const locked = withLedgerLock(file, write);
|
|
12760
|
+
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
12761
|
+
} catch (error) {
|
|
12762
|
+
return err("unavailable", `cannot append to ${file}: ${error.message}`);
|
|
12763
|
+
}
|
|
12764
|
+
}
|
|
12765
|
+
var serialize = (row, now) => {
|
|
12766
|
+
const full = { v: LEDGER_VERSION, id: newId2(), at: now.toISOString(), ...row };
|
|
12767
|
+
return { full, line: `${JSON.stringify(full)}
|
|
12768
|
+
` };
|
|
12769
|
+
};
|
|
12770
|
+
function appendRaw(cwd, row, options) {
|
|
12771
|
+
const { full, line } = serialize(row, options.now ?? new Date);
|
|
12772
|
+
const bytes = Buffer.byteLength(line);
|
|
12773
|
+
if (bytes > MAX_LINE_BYTES)
|
|
12774
|
+
return err("invalid_input", `ledger row is ${bytes} bytes; the limit is ${MAX_LINE_BYTES}. Shorten the text and pass details by --ref <path|url>`);
|
|
12775
|
+
const file = ledgerPath(cwd);
|
|
12776
|
+
if (!file.ok)
|
|
12777
|
+
return file;
|
|
12778
|
+
const written = appendLine(file.value, line, options);
|
|
12779
|
+
return written.ok ? { ok: true, value: full } : written;
|
|
12780
|
+
}
|
|
12781
|
+
function appendHookObserved(cwd, row) {
|
|
12782
|
+
return appendRaw(cwd, { ...row, observer: "host_hook" }, {});
|
|
12783
|
+
}
|
|
12600
12784
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12601
12785
|
var str2 = (value) => typeof value === "string" ? value : null;
|
|
12602
12786
|
var normalizeActor = (value) => {
|
|
@@ -14263,6 +14447,7 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
14263
14447
|
};
|
|
14264
14448
|
|
|
14265
14449
|
// packages/workit-core/src/core/workspaces.ts
|
|
14450
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
14266
14451
|
import { createHash as createHash8 } from "node:crypto";
|
|
14267
14452
|
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
14268
14453
|
import path13 from "node:path";
|
|
@@ -14507,6 +14692,53 @@ var validateWorkspaceGlob = (glob) => {
|
|
|
14507
14692
|
}
|
|
14508
14693
|
return { ok: true };
|
|
14509
14694
|
};
|
|
14695
|
+
|
|
14696
|
+
class WorkspaceConfigError extends Error {
|
|
14697
|
+
name = "WorkspaceConfigError";
|
|
14698
|
+
}
|
|
14699
|
+
var mainCheckouts = new Map;
|
|
14700
|
+
var mainCheckoutOf = (cwd) => {
|
|
14701
|
+
const cached = mainCheckouts.get(cwd);
|
|
14702
|
+
if (cached !== undefined)
|
|
14703
|
+
return cached;
|
|
14704
|
+
const env = { ...process.env };
|
|
14705
|
+
for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
|
|
14706
|
+
delete env[name];
|
|
14707
|
+
const git = (args) => {
|
|
14708
|
+
const run = spawnSync8("git", args, {
|
|
14709
|
+
cwd,
|
|
14710
|
+
env,
|
|
14711
|
+
encoding: "utf8",
|
|
14712
|
+
timeout: 5000,
|
|
14713
|
+
windowsHide: true
|
|
14714
|
+
});
|
|
14715
|
+
return run.status === 0 ? run.stdout : null;
|
|
14716
|
+
};
|
|
14717
|
+
let main = null;
|
|
14718
|
+
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
14719
|
+
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
14720
|
+
if (gitDir && common && path13.resolve(gitDir) !== path13.resolve(common)) {
|
|
14721
|
+
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
14722
|
+
main = listed?.trim() || (path13.basename(common) === ".git" ? path13.dirname(common) : common);
|
|
14723
|
+
}
|
|
14724
|
+
mainCheckouts.set(cwd, main);
|
|
14725
|
+
return main;
|
|
14726
|
+
};
|
|
14727
|
+
var matchingEntries = (cwd, entries) => {
|
|
14728
|
+
const match = (where) => {
|
|
14729
|
+
const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
|
|
14730
|
+
return entries.filter((entry) => {
|
|
14731
|
+
const glob = entry.glob.replaceAll("\\", "/");
|
|
14732
|
+
const canonical = canonicalGlob(glob);
|
|
14733
|
+
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
14734
|
+
});
|
|
14735
|
+
};
|
|
14736
|
+
const direct = match(cwd);
|
|
14737
|
+
if (direct.length)
|
|
14738
|
+
return direct;
|
|
14739
|
+
const main = mainCheckoutOf(cwd);
|
|
14740
|
+
return main ? match(main) : direct;
|
|
14741
|
+
};
|
|
14510
14742
|
var realpathOf = (p) => {
|
|
14511
14743
|
const real = realpathSync3.native ?? realpathSync3;
|
|
14512
14744
|
try {
|
|
@@ -14531,43 +14763,29 @@ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
|
|
|
14531
14763
|
if (named.length === 1)
|
|
14532
14764
|
return named[0];
|
|
14533
14765
|
if (named.length > 1)
|
|
14534
|
-
throw new
|
|
14766
|
+
throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
|
|
14535
14767
|
if (matches.length > 0) {
|
|
14536
|
-
throw new
|
|
14768
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
14537
14769
|
}
|
|
14538
|
-
throw new
|
|
14770
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
14539
14771
|
}
|
|
14540
14772
|
if (matches.length < 2)
|
|
14541
14773
|
return matches[0];
|
|
14542
14774
|
const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
|
|
14543
14775
|
const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
|
|
14544
14776
|
if (mostSpecific.length > 1) {
|
|
14545
|
-
throw new
|
|
14777
|
+
throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
|
|
14546
14778
|
}
|
|
14547
14779
|
return mostSpecific[0];
|
|
14548
14780
|
};
|
|
14549
14781
|
var resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
|
|
14550
|
-
const
|
|
14551
|
-
const matches = [];
|
|
14552
|
-
for (const ws of entries) {
|
|
14553
|
-
const glob = ws.glob.replaceAll("\\", "/");
|
|
14554
|
-
const canonical = canonicalGlob(glob);
|
|
14555
|
-
let matched = false;
|
|
14556
|
-
for (const target of targets) {
|
|
14557
|
-
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
14558
|
-
matched = true;
|
|
14559
|
-
break;
|
|
14560
|
-
}
|
|
14561
|
-
}
|
|
14562
|
-
if (matched)
|
|
14563
|
-
matches.push(ws);
|
|
14564
|
-
}
|
|
14782
|
+
const matches = matchingEntries(cwd, entries);
|
|
14565
14783
|
return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
|
|
14566
14784
|
};
|
|
14567
14785
|
var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
14568
14786
|
const result = readWorkspacesResult(dir);
|
|
14569
14787
|
if (result.status === "malformed" || result.status === "invalid")
|
|
14570
|
-
throw new
|
|
14788
|
+
throw new WorkspaceConfigError(result.error);
|
|
14571
14789
|
return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
|
|
14572
14790
|
};
|
|
14573
14791
|
var runtimeWorkspacePolicySchema = (kind) => {
|
|
@@ -14599,31 +14817,26 @@ var matchRuntimeWorkspace = (cwd) => {
|
|
|
14599
14817
|
} catch (error) {
|
|
14600
14818
|
if (error.code === "ENOENT")
|
|
14601
14819
|
return null;
|
|
14602
|
-
throw new
|
|
14820
|
+
throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
14603
14821
|
}
|
|
14604
14822
|
let parsed;
|
|
14605
14823
|
try {
|
|
14606
14824
|
parsed = JSON.parse(raw);
|
|
14607
14825
|
} catch {
|
|
14608
|
-
throw new
|
|
14826
|
+
throw new WorkspaceConfigError(`${file} is not valid JSON`);
|
|
14609
14827
|
}
|
|
14610
14828
|
const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
|
|
14611
14829
|
if (!validated.success) {
|
|
14612
14830
|
const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
|
|
14613
|
-
throw new
|
|
14831
|
+
throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
|
|
14614
14832
|
}
|
|
14615
14833
|
const entries = validated.data.workspaces;
|
|
14616
14834
|
for (const entry of entries ?? []) {
|
|
14617
14835
|
const glob = validateWorkspaceGlob(entry.glob);
|
|
14618
14836
|
if (!glob.ok)
|
|
14619
|
-
throw new
|
|
14837
|
+
throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
14620
14838
|
}
|
|
14621
|
-
const
|
|
14622
|
-
const matches = (entries ?? []).filter((entry) => {
|
|
14623
|
-
const glob = entry.glob.replaceAll("\\", "/");
|
|
14624
|
-
const canonical = canonicalGlob(glob);
|
|
14625
|
-
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
14626
|
-
});
|
|
14839
|
+
const matches = matchingEntries(cwd, entries ?? []);
|
|
14627
14840
|
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
14628
14841
|
const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
|
|
14629
14842
|
return selected ? { file, selected } : null;
|
|
@@ -14640,7 +14853,7 @@ var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
|
14640
14853
|
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
14641
14854
|
if (!policy.success) {
|
|
14642
14855
|
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
14643
|
-
throw new
|
|
14856
|
+
throw new WorkspaceConfigError(`${file} has invalid ${kind} policy configuration: ${detail}`);
|
|
14644
14857
|
}
|
|
14645
14858
|
return policy.data;
|
|
14646
14859
|
};
|
|
@@ -14651,10 +14864,10 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
|
14651
14864
|
const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
|
|
14652
14865
|
const profileName = requestedProfile?.trim() || candidate.defaultProfile;
|
|
14653
14866
|
if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
|
|
14654
|
-
throw new
|
|
14867
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
|
|
14655
14868
|
const profile = profileName ? candidate.profiles?.[profileName] : undefined;
|
|
14656
14869
|
if (profileName && !profile)
|
|
14657
|
-
throw new
|
|
14870
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
|
|
14658
14871
|
const profilePolicy = profile?.[key];
|
|
14659
14872
|
const workspacePolicy = candidate[key];
|
|
14660
14873
|
return {
|
|
@@ -14674,11 +14887,11 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
14674
14887
|
// packages/workit-core/src/core/vcs-config.ts
|
|
14675
14888
|
import fs12 from "node:fs";
|
|
14676
14889
|
import path14 from "node:path";
|
|
14677
|
-
import { spawnSync as
|
|
14890
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14678
14891
|
|
|
14679
14892
|
// packages/workit-core/src/core/release-tracks.ts
|
|
14680
14893
|
import { createHash as createHash9 } from "node:crypto";
|
|
14681
|
-
import { spawnSync as
|
|
14894
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
14682
14895
|
class ReleaseTrackError extends Error {
|
|
14683
14896
|
}
|
|
14684
14897
|
var KNOWN_FIELDS = new Set([
|
|
@@ -14963,7 +15176,7 @@ function resolveReleaseTrack(input) {
|
|
|
14963
15176
|
return fallback(`${branch} has the same or unrelated fork points with ${[...new Set(newest.map((fork) => `${fork.ref} (${fork.track.name === IMPLICIT ? "workspace default" : fork.track.name})`))].join(" and ")}`);
|
|
14964
15177
|
}
|
|
14965
15178
|
var git4 = (cwd, args) => {
|
|
14966
|
-
const run =
|
|
15179
|
+
const run = spawnSync9("git", args, {
|
|
14967
15180
|
cwd,
|
|
14968
15181
|
encoding: "utf8",
|
|
14969
15182
|
timeout: 20000,
|
|
@@ -15078,7 +15291,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
15078
15291
|
var workspacesPath = () => path14.join(configDir(), "workspaces.json");
|
|
15079
15292
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
15080
15293
|
var remoteProvider = (cwd) => {
|
|
15081
|
-
const r =
|
|
15294
|
+
const r = spawnSync10("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
15082
15295
|
if (r.status !== 0)
|
|
15083
15296
|
return null;
|
|
15084
15297
|
const url = (r.stdout ?? "").trim();
|
|
@@ -15115,7 +15328,7 @@ var remoteEndpoint = (remote) => {
|
|
|
15115
15328
|
var configuredSshEndpoint = (host) => {
|
|
15116
15329
|
if (host.startsWith("-"))
|
|
15117
15330
|
return null;
|
|
15118
|
-
const resolved =
|
|
15331
|
+
const resolved = spawnSync10("ssh", ["-G", host], {
|
|
15119
15332
|
encoding: "utf8",
|
|
15120
15333
|
timeout: 5000,
|
|
15121
15334
|
maxBuffer: 1024 * 1024,
|
|
@@ -15288,7 +15501,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
15288
15501
|
`).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
|
|
15289
15502
|
descriptionPreview: desc.slice(0, 600)
|
|
15290
15503
|
});
|
|
15291
|
-
const remoteResult =
|
|
15504
|
+
const remoteResult = spawnSync10("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
15292
15505
|
cwd,
|
|
15293
15506
|
encoding: "utf8"
|
|
15294
15507
|
});
|
|
@@ -15304,7 +15517,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
15304
15517
|
if (!hostingApiHostMatches(remote, host))
|
|
15305
15518
|
return { ok: false, error: "PR style target does not match the provider host" };
|
|
15306
15519
|
if (provider === "gitlab") {
|
|
15307
|
-
const run = (args) =>
|
|
15520
|
+
const run = (args) => spawnSync10("glab", ["api", ...args], {
|
|
15308
15521
|
cwd,
|
|
15309
15522
|
encoding: "utf8",
|
|
15310
15523
|
env
|
|
@@ -15324,7 +15537,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
15324
15537
|
});
|
|
15325
15538
|
}
|
|
15326
15539
|
} else if (provider === "github") {
|
|
15327
|
-
const r =
|
|
15540
|
+
const r = spawnSync10("gh", [
|
|
15328
15541
|
"pr",
|
|
15329
15542
|
"list",
|
|
15330
15543
|
"--repo",
|
|
@@ -15448,6 +15661,7 @@ var validateBranchNameFor = (workspaceRoot, name) => {
|
|
|
15448
15661
|
};
|
|
15449
15662
|
}
|
|
15450
15663
|
};
|
|
15664
|
+
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
15451
15665
|
|
|
15452
15666
|
// packages/workit-core/src/core/route-intent.ts
|
|
15453
15667
|
var shellBranchTarget = (command) => {
|
|
@@ -17370,7 +17584,7 @@ class WorkitCore {
|
|
|
17370
17584
|
}
|
|
17371
17585
|
}
|
|
17372
17586
|
// packages/workit-core/src/core/repo-context.ts
|
|
17373
|
-
import { spawnSync as
|
|
17587
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17374
17588
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
17375
17589
|
import path16 from "node:path";
|
|
17376
17590
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
@@ -17399,7 +17613,7 @@ var invalidRange = (cwd) => ({
|
|
|
17399
17613
|
cwd
|
|
17400
17614
|
});
|
|
17401
17615
|
var runGit = (cwd, args) => {
|
|
17402
|
-
const result =
|
|
17616
|
+
const result = spawnSync11("git", args, { cwd, encoding: "utf8" });
|
|
17403
17617
|
return {
|
|
17404
17618
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
17405
17619
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -17840,12 +18054,12 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
17840
18054
|
}
|
|
17841
18055
|
|
|
17842
18056
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
17843
|
-
import { spawnSync as
|
|
18057
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
17844
18058
|
|
|
17845
18059
|
// packages/workit-core/src/core/pr-create.ts
|
|
17846
18060
|
import fs13 from "node:fs";
|
|
17847
18061
|
import path17 from "node:path";
|
|
17848
|
-
import { spawnSync as
|
|
18062
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
17849
18063
|
// packages/workit-core/src/forge/pr-body.ts
|
|
17850
18064
|
function parseGhIssue(value) {
|
|
17851
18065
|
const m = /issues\/(\d+)/.exec(value);
|
|
@@ -17870,7 +18084,7 @@ function whichOnPath(tool) {
|
|
|
17870
18084
|
}
|
|
17871
18085
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
17872
18086
|
var pushRemote = (cwd) => {
|
|
17873
|
-
const result =
|
|
18087
|
+
const result = spawnSync12("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
17874
18088
|
cwd,
|
|
17875
18089
|
encoding: "utf8"
|
|
17876
18090
|
});
|
|
@@ -17901,7 +18115,7 @@ var defaultCli = (bin, available) => {
|
|
|
17901
18115
|
if (!available)
|
|
17902
18116
|
return null;
|
|
17903
18117
|
return (args, root) => {
|
|
17904
|
-
const out =
|
|
18118
|
+
const out = spawnSync13(bin, args, {
|
|
17905
18119
|
cwd: root,
|
|
17906
18120
|
encoding: "utf8",
|
|
17907
18121
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -18434,7 +18648,7 @@ function resolveInside(root, candidate) {
|
|
|
18434
18648
|
}
|
|
18435
18649
|
function run2(root, executable, args, env = {}) {
|
|
18436
18650
|
const cwd = realpathSync5(root);
|
|
18437
|
-
const result =
|
|
18651
|
+
const result = spawnSync14(executable, args, {
|
|
18438
18652
|
cwd,
|
|
18439
18653
|
encoding: "utf8",
|
|
18440
18654
|
env: { ...process.env, ...env }
|
|
@@ -18626,36 +18840,43 @@ var shellPolicy = (cwd, command) => {
|
|
|
18626
18840
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
18627
18841
|
import { realpathSync as realpathSync6 } from "node:fs";
|
|
18628
18842
|
import path21 from "node:path";
|
|
18629
|
-
|
|
18630
|
-
|
|
18631
|
-
var
|
|
18632
|
-
|
|
18633
|
-
for (let
|
|
18634
|
-
|
|
18635
|
-
|
|
18636
|
-
|
|
18637
|
-
|
|
18638
|
-
|
|
18639
|
-
|
|
18843
|
+
|
|
18844
|
+
// packages/workit-core/src/hooks/shell-words.ts
|
|
18845
|
+
var closingQuote = (command, start, dialect) => {
|
|
18846
|
+
const quote = command[start];
|
|
18847
|
+
for (let index = start + 1;index < command.length; index++) {
|
|
18848
|
+
const char = command[index];
|
|
18849
|
+
if (quote === "'") {
|
|
18850
|
+
if (char !== "'")
|
|
18851
|
+
continue;
|
|
18852
|
+
if (dialect === "powershell" && command[index + 1] === "'") {
|
|
18853
|
+
index++;
|
|
18854
|
+
continue;
|
|
18855
|
+
}
|
|
18856
|
+
return index;
|
|
18640
18857
|
}
|
|
18858
|
+
if (dialect === "posix" && char === "\\" || dialect === "powershell" && char === "`") {
|
|
18859
|
+
index++;
|
|
18860
|
+
continue;
|
|
18861
|
+
}
|
|
18862
|
+
if (char === '"')
|
|
18863
|
+
return index;
|
|
18641
18864
|
}
|
|
18865
|
+
return -1;
|
|
18642
18866
|
};
|
|
18643
|
-
var
|
|
18644
|
-
|
|
18645
|
-
|
|
18646
|
-
|
|
18647
|
-
return true;
|
|
18648
|
-
const posix = relative.split(path21.sep).join("/");
|
|
18649
|
-
const segments = posix.split("/");
|
|
18650
|
-
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
18867
|
+
var unquote = (body, quote, dialect) => {
|
|
18868
|
+
if (quote === "'")
|
|
18869
|
+
return dialect === "powershell" ? body.replaceAll("''", "'") : body;
|
|
18870
|
+
return dialect === "posix" ? body.replace(/\\(["\\$`])/g, "$1") : body.replace(/`(.)/g, "$1");
|
|
18651
18871
|
};
|
|
18652
|
-
var segmentsOf = (command) => {
|
|
18872
|
+
var segmentsOf = (command, dialect = "posix") => {
|
|
18653
18873
|
const segments = [];
|
|
18654
18874
|
let current = { words: [], redirects: [] };
|
|
18655
18875
|
let word = "";
|
|
18656
18876
|
let inWord = false;
|
|
18657
18877
|
let pending = null;
|
|
18658
18878
|
const heredocs = [];
|
|
18879
|
+
const escape = dialect === "posix" ? "\\" : "`";
|
|
18659
18880
|
const flushWord = () => {
|
|
18660
18881
|
if (!inWord)
|
|
18661
18882
|
return;
|
|
@@ -18679,11 +18900,11 @@ var segmentsOf = (command) => {
|
|
|
18679
18900
|
for (let index = 0;index < command.length; index++) {
|
|
18680
18901
|
const char = command[index];
|
|
18681
18902
|
if (char === "'" || char === '"') {
|
|
18682
|
-
const end = command
|
|
18683
|
-
word += command.slice(index + 1, end < 0 ? undefined : end);
|
|
18903
|
+
const end = closingQuote(command, index, dialect);
|
|
18904
|
+
word += unquote(command.slice(index + 1, end < 0 ? undefined : end), char, dialect);
|
|
18684
18905
|
inWord = true;
|
|
18685
18906
|
index = end < 0 ? command.length : end;
|
|
18686
|
-
} else if (char ===
|
|
18907
|
+
} else if (char === escape && index + 1 < command.length) {
|
|
18687
18908
|
word += command[++index];
|
|
18688
18909
|
inWord = true;
|
|
18689
18910
|
} else if (char === `
|
|
@@ -18700,7 +18921,14 @@ var segmentsOf = (command) => {
|
|
|
18700
18921
|
}
|
|
18701
18922
|
} else if (/\s/.test(char))
|
|
18702
18923
|
flushWord();
|
|
18703
|
-
else if ("
|
|
18924
|
+
else if (char === "(" || char === ")") {
|
|
18925
|
+
if (char === "(" && inWord && word.endsWith("$")) {
|
|
18926
|
+
word = word.slice(0, -1);
|
|
18927
|
+
inWord = word !== "";
|
|
18928
|
+
}
|
|
18929
|
+
flushSegment();
|
|
18930
|
+
segments.push({ words: [], redirects: [], group: char === "(" ? "open" : "close" });
|
|
18931
|
+
} else if (";&|".includes(char)) {
|
|
18704
18932
|
if (char === "&" && command[index + 1] === ">")
|
|
18705
18933
|
continue;
|
|
18706
18934
|
flushSegment();
|
|
@@ -18718,7 +18946,7 @@ var segmentsOf = (command) => {
|
|
|
18718
18946
|
inWord = true;
|
|
18719
18947
|
}
|
|
18720
18948
|
pending = "redirect";
|
|
18721
|
-
} else if (char === "<") {
|
|
18949
|
+
} else if (char === "<" && dialect === "posix") {
|
|
18722
18950
|
flushWord();
|
|
18723
18951
|
if (command[index + 1] === "<") {
|
|
18724
18952
|
index++;
|
|
@@ -18735,6 +18963,31 @@ var segmentsOf = (command) => {
|
|
|
18735
18963
|
flushSegment();
|
|
18736
18964
|
return segments;
|
|
18737
18965
|
};
|
|
18966
|
+
|
|
18967
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
18968
|
+
var NONE2 = { kind: "none" };
|
|
18969
|
+
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
18970
|
+
var canonical2 = (file) => {
|
|
18971
|
+
let rest = "";
|
|
18972
|
+
for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
|
|
18973
|
+
try {
|
|
18974
|
+
return path21.join(realpathSync6(dir), rest);
|
|
18975
|
+
} catch {
|
|
18976
|
+
if (path21.dirname(dir) === dir)
|
|
18977
|
+
return path21.resolve(file);
|
|
18978
|
+
rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
|
|
18979
|
+
}
|
|
18980
|
+
}
|
|
18981
|
+
};
|
|
18982
|
+
var exempt = (cwd, root, file, plans) => {
|
|
18983
|
+
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
18984
|
+
const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
|
|
18985
|
+
if (relative.startsWith("..") || path21.isAbsolute(relative))
|
|
18986
|
+
return true;
|
|
18987
|
+
const posix = relative.split(path21.sep).join("/");
|
|
18988
|
+
const segments = posix.split("/");
|
|
18989
|
+
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
18990
|
+
};
|
|
18738
18991
|
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
18739
18992
|
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
18740
18993
|
var editsOf = (input) => {
|
|
@@ -18884,10 +19137,469 @@ function writeGate(cwd, paths) {
|
|
|
18884
19137
|
return NONE2;
|
|
18885
19138
|
}
|
|
18886
19139
|
}
|
|
19140
|
+
// packages/workit-core/src/hooks/raw-git.ts
|
|
19141
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
19142
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
19143
|
+
import fs15 from "node:fs";
|
|
19144
|
+
import os4 from "node:os";
|
|
19145
|
+
import path22 from "node:path";
|
|
19146
|
+
var NONE3 = { kind: "none" };
|
|
19147
|
+
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
19148
|
+
var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
19149
|
+
var SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
|
|
19150
|
+
var CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
19151
|
+
var PUSHD = new Set(["pushd", "push-location"]);
|
|
19152
|
+
var POPD = new Set(["popd", "pop-location"]);
|
|
19153
|
+
var GIT_VALUE_OPTIONS = new Set([
|
|
19154
|
+
"-c",
|
|
19155
|
+
"--git-dir",
|
|
19156
|
+
"--work-tree",
|
|
19157
|
+
"--namespace",
|
|
19158
|
+
"--exec-path",
|
|
19159
|
+
"--config-env",
|
|
19160
|
+
"--super-prefix"
|
|
19161
|
+
]);
|
|
19162
|
+
var isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
19163
|
+
var joinDir = (base, next) => {
|
|
19164
|
+
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
19165
|
+
return;
|
|
19166
|
+
const expanded = next === "~" || next.startsWith("~/") ? path22.join(os4.homedir(), next.slice(1)) : next;
|
|
19167
|
+
return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
|
|
19168
|
+
};
|
|
19169
|
+
var cdTarget = (words) => {
|
|
19170
|
+
const args = words.slice(1);
|
|
19171
|
+
const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
|
|
19172
|
+
if (flag >= 0)
|
|
19173
|
+
return args[flag + 1] ?? null;
|
|
19174
|
+
return args.find((word) => word === "-" || !word.startsWith("-")) ?? null;
|
|
19175
|
+
};
|
|
19176
|
+
var stripPrefixes = (input) => {
|
|
19177
|
+
let words = input;
|
|
19178
|
+
for (;; ) {
|
|
19179
|
+
const first = words[0];
|
|
19180
|
+
if (first === undefined)
|
|
19181
|
+
return words;
|
|
19182
|
+
if (/^\w+=/.test(first))
|
|
19183
|
+
words = words.slice(1);
|
|
19184
|
+
else if (PREFIXES3.has(first)) {
|
|
19185
|
+
words = words.slice(1);
|
|
19186
|
+
while (words[0]?.startsWith("-"))
|
|
19187
|
+
words = words.slice(words[0] === "-u" ? 2 : 1);
|
|
19188
|
+
} else if (first === "timeout") {
|
|
19189
|
+
words = words.slice(1);
|
|
19190
|
+
while (words[0]?.startsWith("-"))
|
|
19191
|
+
words = words.slice(1);
|
|
19192
|
+
words = words.slice(1);
|
|
19193
|
+
} else
|
|
19194
|
+
return words;
|
|
19195
|
+
}
|
|
19196
|
+
};
|
|
19197
|
+
var commandName = (word) => (word?.split(/[\\/]/).at(-1) ?? "").toLowerCase().replace(/\.exe$/, "");
|
|
19198
|
+
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
19199
|
+
const found = [];
|
|
19200
|
+
let dir = null;
|
|
19201
|
+
const groups = [];
|
|
19202
|
+
let stack = [];
|
|
19203
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
19204
|
+
if (segment.group === "open") {
|
|
19205
|
+
groups.push({ dir, stack: [...stack] });
|
|
19206
|
+
continue;
|
|
19207
|
+
}
|
|
19208
|
+
if (segment.group === "close") {
|
|
19209
|
+
const saved = groups.pop();
|
|
19210
|
+
if (saved)
|
|
19211
|
+
({ dir, stack } = saved);
|
|
19212
|
+
continue;
|
|
19213
|
+
}
|
|
19214
|
+
const words = stripPrefixes(segment.words);
|
|
19215
|
+
const name = commandName(words[0]);
|
|
19216
|
+
if (CD.has(name)) {
|
|
19217
|
+
const target = cdTarget(words);
|
|
19218
|
+
dir = target === null ? os4.homedir() : joinDir(dir, target);
|
|
19219
|
+
continue;
|
|
19220
|
+
}
|
|
19221
|
+
if (PUSHD.has(name)) {
|
|
19222
|
+
const target = cdTarget(words);
|
|
19223
|
+
stack.push(dir);
|
|
19224
|
+
dir = target === null || target === "-" ? undefined : joinDir(dir, target);
|
|
19225
|
+
continue;
|
|
19226
|
+
}
|
|
19227
|
+
if (POPD.has(name)) {
|
|
19228
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
19229
|
+
continue;
|
|
19230
|
+
}
|
|
19231
|
+
if (SHELLS.has(name) && depth < 3) {
|
|
19232
|
+
const flag = words.findIndex((word, index) => index > 0 && SCRIPT_FLAG.test(word));
|
|
19233
|
+
const script = flag > 0 ? words[flag + 1] : undefined;
|
|
19234
|
+
if (script)
|
|
19235
|
+
for (const inner of rawInvocations(script, "posix", depth + 1))
|
|
19236
|
+
found.push({
|
|
19237
|
+
...inner,
|
|
19238
|
+
dir: inner.dir === null ? dir : inner.dir === undefined ? undefined : joinDir(dir, inner.dir)
|
|
19239
|
+
});
|
|
19240
|
+
continue;
|
|
19241
|
+
}
|
|
19242
|
+
if (name === "gh" || name === "glab") {
|
|
19243
|
+
found.push({ tool: name, dir, args: words.slice(1) });
|
|
19244
|
+
continue;
|
|
19245
|
+
}
|
|
19246
|
+
if (name !== "git")
|
|
19247
|
+
continue;
|
|
19248
|
+
let here = dir;
|
|
19249
|
+
let index = 1;
|
|
19250
|
+
for (;index < words.length; index++) {
|
|
19251
|
+
const word = words[index];
|
|
19252
|
+
if (word === "-C")
|
|
19253
|
+
here = words[index + 1] ? joinDir(here, words[++index]) : here;
|
|
19254
|
+
else if (GIT_VALUE_OPTIONS.has(word))
|
|
19255
|
+
index++;
|
|
19256
|
+
else if (!word.startsWith("-"))
|
|
19257
|
+
break;
|
|
19258
|
+
}
|
|
19259
|
+
found.push({ tool: "git", dir: here, args: words.slice(index) });
|
|
19260
|
+
}
|
|
19261
|
+
return found;
|
|
19262
|
+
}
|
|
19263
|
+
var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
19264
|
+
var pushAction = (args) => {
|
|
19265
|
+
let force = false;
|
|
19266
|
+
let lease = false;
|
|
19267
|
+
let wholesale = false;
|
|
19268
|
+
let remote = null;
|
|
19269
|
+
const positionals = [];
|
|
19270
|
+
for (let index = 1;index < args.length; index++) {
|
|
19271
|
+
const arg = args[index];
|
|
19272
|
+
if (arg === "--") {
|
|
19273
|
+
positionals.push(...args.slice(index + 1));
|
|
19274
|
+
break;
|
|
19275
|
+
}
|
|
19276
|
+
if (arg === "--dry-run" || /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg))
|
|
19277
|
+
return null;
|
|
19278
|
+
if (arg === "--repo")
|
|
19279
|
+
remote = args[++index] ?? null;
|
|
19280
|
+
else if (arg.startsWith("--repo="))
|
|
19281
|
+
remote = arg.slice("--repo=".length);
|
|
19282
|
+
else if (PUSH_VALUE_OPTIONS.has(arg))
|
|
19283
|
+
index++;
|
|
19284
|
+
else if (arg === "--force" || /^-[a-zA-Z]*f[a-zA-Z]*$/.test(arg))
|
|
19285
|
+
force = true;
|
|
19286
|
+
else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
|
|
19287
|
+
lease = true;
|
|
19288
|
+
else if (["--all", "--mirror", "--branches", "--tags"].includes(arg))
|
|
19289
|
+
wholesale = true;
|
|
19290
|
+
else if (!arg.startsWith("-"))
|
|
19291
|
+
positionals.push(arg);
|
|
19292
|
+
}
|
|
19293
|
+
const specs = remote === null ? positionals.slice(1) : positionals;
|
|
19294
|
+
remote ??= positionals[0] ?? null;
|
|
19295
|
+
const targets = [];
|
|
19296
|
+
for (const raw of specs) {
|
|
19297
|
+
if (raw.startsWith("+"))
|
|
19298
|
+
force = true;
|
|
19299
|
+
const spec = raw.replace(/^\+/, "");
|
|
19300
|
+
const colon = spec.indexOf(":");
|
|
19301
|
+
const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
|
|
19302
|
+
if (!destination)
|
|
19303
|
+
continue;
|
|
19304
|
+
if (destination === "HEAD")
|
|
19305
|
+
targets.push(null);
|
|
19306
|
+
else if (destination.startsWith("refs/heads/"))
|
|
19307
|
+
targets.push(destination.slice(11));
|
|
19308
|
+
else if (!destination.startsWith("refs/"))
|
|
19309
|
+
targets.push(destination);
|
|
19310
|
+
}
|
|
19311
|
+
if (specs.length === 0 && !wholesale)
|
|
19312
|
+
targets.push(null);
|
|
19313
|
+
return { kind: "push", targets, blindForce: force && !lease, remote };
|
|
19314
|
+
};
|
|
19315
|
+
var VALUE_OPTIONS = {
|
|
19316
|
+
git: new Set([
|
|
19317
|
+
"-m",
|
|
19318
|
+
"--message",
|
|
19319
|
+
"-F",
|
|
19320
|
+
"--file",
|
|
19321
|
+
"-C",
|
|
19322
|
+
"-c",
|
|
19323
|
+
"--reuse-message",
|
|
19324
|
+
"--reedit-message",
|
|
19325
|
+
"--author",
|
|
19326
|
+
"--date",
|
|
19327
|
+
"--trailer",
|
|
19328
|
+
"-t",
|
|
19329
|
+
"--template",
|
|
19330
|
+
"--cleanup",
|
|
19331
|
+
"--fixup",
|
|
19332
|
+
"--squash",
|
|
19333
|
+
"-o",
|
|
19334
|
+
"--push-option",
|
|
19335
|
+
"--repo",
|
|
19336
|
+
"--receive-pack",
|
|
19337
|
+
"--exec"
|
|
19338
|
+
]),
|
|
19339
|
+
gh: new Set(["-b", "--body", "-F", "--body-file", "-t", "--title", "--subject", "-A", "--author-email"].concat(["-B", "--base", "-H", "--head", "-l", "--label", "-a", "--assignee"]).concat(["-r", "--reviewer", "-m", "--milestone", "-p", "--project", "-T", "--template"]).concat(["--match-head-commit", "-R", "--repo"])),
|
|
19340
|
+
glab: new Set(["-m", "--message", "--squash-message", "--sha", "-t", "--title", "-d", "--description"].concat(["-b", "--target-branch", "-s", "--source-branch", "-l", "--label"]).concat(["-a", "--assignee", "--reviewer", "-R", "--repo"]))
|
|
19341
|
+
};
|
|
19342
|
+
var asksHelp = (invocation) => {
|
|
19343
|
+
const { tool, args } = invocation;
|
|
19344
|
+
if (args[0] === "help" || tool !== "git" && args[1] === "help")
|
|
19345
|
+
return true;
|
|
19346
|
+
const values = VALUE_OPTIONS[tool];
|
|
19347
|
+
for (let index = 0;index < args.length; index++) {
|
|
19348
|
+
const arg = args[index];
|
|
19349
|
+
if (arg === "--")
|
|
19350
|
+
return false;
|
|
19351
|
+
if (arg === "--help" || arg === "-h")
|
|
19352
|
+
return true;
|
|
19353
|
+
if (values.has(arg))
|
|
19354
|
+
index++;
|
|
19355
|
+
}
|
|
19356
|
+
return false;
|
|
19357
|
+
};
|
|
19358
|
+
function rawAction(invocation) {
|
|
19359
|
+
const args = invocation.args;
|
|
19360
|
+
if (asksHelp(invocation))
|
|
19361
|
+
return null;
|
|
19362
|
+
const [first, second] = args;
|
|
19363
|
+
if (invocation.tool === "git") {
|
|
19364
|
+
if (first === "commit")
|
|
19365
|
+
return args.includes("--dry-run") ? null : { kind: "commit", amend: args.includes("--amend") };
|
|
19366
|
+
if (first === "push")
|
|
19367
|
+
return pushAction(args);
|
|
19368
|
+
return null;
|
|
19369
|
+
}
|
|
19370
|
+
const forge = invocation.tool;
|
|
19371
|
+
const noun = forge === "gh" ? "pr" : "mr";
|
|
19372
|
+
if (first !== noun)
|
|
19373
|
+
return null;
|
|
19374
|
+
if (second === "merge")
|
|
19375
|
+
return { kind: "merge", forge };
|
|
19376
|
+
if (second === "create")
|
|
19377
|
+
return { kind: "pr-create", forge };
|
|
19378
|
+
if (["view", "checks", "status", "ci", "list"].includes(second ?? ""))
|
|
19379
|
+
return { kind: "pr-read", forge };
|
|
19380
|
+
return null;
|
|
19381
|
+
}
|
|
19382
|
+
var repoAt = (dir) => {
|
|
19383
|
+
const location = resolveStore(dir);
|
|
19384
|
+
if (location instanceof Error || !location.shared)
|
|
19385
|
+
return null;
|
|
19386
|
+
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
19387
|
+
};
|
|
19388
|
+
var isWorkitWorkspace = (repo) => {
|
|
19389
|
+
if (fs15.existsSync(repo.location.dir))
|
|
19390
|
+
return true;
|
|
19391
|
+
try {
|
|
19392
|
+
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
19393
|
+
} catch {
|
|
19394
|
+
return false;
|
|
19395
|
+
}
|
|
19396
|
+
};
|
|
19397
|
+
var guardedBranch = (dir, branch) => {
|
|
19398
|
+
try {
|
|
19399
|
+
const policy = resolveBranchPolicyFor(dir);
|
|
19400
|
+
return policy.protected.has(branch.toLowerCase()) || policy.defaultTargetBranch.toLowerCase() === branch.toLowerCase();
|
|
19401
|
+
} catch {
|
|
19402
|
+
return false;
|
|
19403
|
+
}
|
|
19404
|
+
};
|
|
19405
|
+
var toPushRemote = (repo, remote) => {
|
|
19406
|
+
if (remote === null)
|
|
19407
|
+
return true;
|
|
19408
|
+
try {
|
|
19409
|
+
const pushRemote = pushRemoteName(repo.dir, repo.branch);
|
|
19410
|
+
return pushRemote === null || pushRemote === remote;
|
|
19411
|
+
} catch {
|
|
19412
|
+
return false;
|
|
19413
|
+
}
|
|
19414
|
+
};
|
|
19415
|
+
var headSha2 = (repo) => {
|
|
19416
|
+
const git = repo.location.git;
|
|
19417
|
+
if (!git || git.reftable)
|
|
19418
|
+
return null;
|
|
19419
|
+
try {
|
|
19420
|
+
const head = fs15.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
19421
|
+
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
19422
|
+
return head;
|
|
19423
|
+
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
19424
|
+
if (!ref)
|
|
19425
|
+
return null;
|
|
19426
|
+
const common = path22.dirname(repo.location.dir);
|
|
19427
|
+
try {
|
|
19428
|
+
return fs15.readFileSync(path22.join(common, ref), "utf8").trim() || null;
|
|
19429
|
+
} catch {}
|
|
19430
|
+
const packed = fs15.readFileSync(path22.join(common, "packed-refs"), "utf8");
|
|
19431
|
+
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
19432
|
+
} catch {
|
|
19433
|
+
return null;
|
|
19434
|
+
}
|
|
19435
|
+
};
|
|
19436
|
+
var quote = (text) => `\`${text}\``;
|
|
19437
|
+
var refusal = (action, branch) => {
|
|
19438
|
+
if (action.kind === "merge")
|
|
19439
|
+
return `workit: raw ${quote(action.forge === "gh" ? "gh pr merge" : "glab mr merge")} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
|
|
19440
|
+
return `workit: raw ${quote("git push")}${action.kind === "push" && action.blindForce ? " --force" : ""} onto ${branch}, a protected branch or the default target, bypasses workit's protected-branch check. Push your feature branch with ${quote("workit git push")} (${quote("workit git push --force-with-lease")} to rewrite it) and land it with ${quote("workit pr merge")}.`;
|
|
19441
|
+
};
|
|
19442
|
+
var nudgeFor = (action, host, session) => {
|
|
19443
|
+
switch (action.kind) {
|
|
19444
|
+
case "commit": {
|
|
19445
|
+
const prefix = host === "cursor" && session ? `WORKIT_SESSION_ID=${session} ` : "";
|
|
19446
|
+
return action.amend ? `workit: raw ${quote("git commit --amend")} is recorded for this session; for new commits prefer ${quote(`${prefix}workit git commit -m '<msg>'`)} (adds the Workit-Session trailer and checks branch and commit policy).` : `workit: prefer ${quote(`${prefix}workit git commit -m '<msg>' [--all | -- <paths>]`)}: it adds the Workit-Session trailer and checks branch and commit policy.`;
|
|
19447
|
+
}
|
|
19448
|
+
case "push":
|
|
19449
|
+
return action.blindForce ? `workit: ${quote("git push --force")} overwrites without a lease; use ${quote("workit git push --force-with-lease")}.` : `workit: prefer ${quote("workit git push")}: it refuses protected branches and verifies the remote tip.`;
|
|
19450
|
+
case "pr-create":
|
|
19451
|
+
return `workit: prefer ${quote("workit pr create --fill")} (or --title/--body): it records the PR the merge gate checks.`;
|
|
19452
|
+
case "pr-read":
|
|
19453
|
+
return `workit: ${quote("workit pr status")} shows the PR, its checks and the verdict state; ${quote("workit ci wait")} waits for checks.`;
|
|
19454
|
+
case "merge":
|
|
19455
|
+
return null;
|
|
19456
|
+
}
|
|
19457
|
+
};
|
|
19458
|
+
var actionsIn = (cwd, command, dialect) => {
|
|
19459
|
+
const out = [];
|
|
19460
|
+
const repos = new Map;
|
|
19461
|
+
for (const invocation of rawInvocations(command, dialect)) {
|
|
19462
|
+
if (invocation.dir === undefined)
|
|
19463
|
+
continue;
|
|
19464
|
+
const action = rawAction(invocation);
|
|
19465
|
+
if (!action)
|
|
19466
|
+
continue;
|
|
19467
|
+
const dir = path22.resolve(cwd, invocation.dir ?? ".");
|
|
19468
|
+
if (!repos.has(dir)) {
|
|
19469
|
+
const repo = repoAt(dir);
|
|
19470
|
+
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
19471
|
+
}
|
|
19472
|
+
const repo = repos.get(dir);
|
|
19473
|
+
if (repo)
|
|
19474
|
+
out.push({ action, repo });
|
|
19475
|
+
}
|
|
19476
|
+
return out;
|
|
19477
|
+
};
|
|
19478
|
+
var mentionsRawTool = (command) => /\b(?:git|gh|glab)\b/i.test(command);
|
|
19479
|
+
var dialectOf = (input) => (input.event.kind === "shell.pre" || input.event.kind === "shell.post" ? input.event.dialect : undefined) ?? "posix";
|
|
19480
|
+
function rawGitPre(input, command) {
|
|
19481
|
+
try {
|
|
19482
|
+
if (!mentionsRawTool(command))
|
|
19483
|
+
return NONE3;
|
|
19484
|
+
const nudges = [];
|
|
19485
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
|
|
19486
|
+
if (action.kind === "merge")
|
|
19487
|
+
return deny(refusal(action, null));
|
|
19488
|
+
if (action.kind === "push")
|
|
19489
|
+
for (const target of action.targets) {
|
|
19490
|
+
const branch = target ?? repo.branch;
|
|
19491
|
+
if (branch && guardedBranch(repo.dir, branch) && toPushRemote(repo, action.remote))
|
|
19492
|
+
return deny(refusal(action, branch));
|
|
19493
|
+
}
|
|
19494
|
+
const nudge = nudgeFor(action, input.host, input.session.id);
|
|
19495
|
+
if (nudge && !nudges.includes(nudge))
|
|
19496
|
+
nudges.push(nudge);
|
|
19497
|
+
}
|
|
19498
|
+
return nudges.length ? { kind: "context", text: nudges.join(`
|
|
19499
|
+
`) } : NONE3;
|
|
19500
|
+
} catch {
|
|
19501
|
+
return NONE3;
|
|
19502
|
+
}
|
|
19503
|
+
}
|
|
19504
|
+
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
19505
|
+
var NEXT_COMMAND = "next";
|
|
19506
|
+
var MARKER_TTL_SECONDS = 3600;
|
|
19507
|
+
var markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash11("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
19508
|
+
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash11("sha256").update(command).digest("hex")}`;
|
|
19509
|
+
function noteRawCommit(input, command, key) {
|
|
19510
|
+
try {
|
|
19511
|
+
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
19512
|
+
return;
|
|
19513
|
+
const at = Math.floor(Date.now() / 1000);
|
|
19514
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input))) {
|
|
19515
|
+
if (action.kind !== "commit")
|
|
19516
|
+
continue;
|
|
19517
|
+
const file = markerFile(repo, input.session.id, key);
|
|
19518
|
+
fs15.mkdirSync(path22.dirname(file), { recursive: true });
|
|
19519
|
+
fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
19520
|
+
`);
|
|
19521
|
+
}
|
|
19522
|
+
} catch {}
|
|
19523
|
+
}
|
|
19524
|
+
var headCommit = (dir) => {
|
|
19525
|
+
const run = spawnSync15("git", [
|
|
19526
|
+
"log",
|
|
19527
|
+
"-1",
|
|
19528
|
+
"--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
|
|
19529
|
+
], {
|
|
19530
|
+
cwd: dir,
|
|
19531
|
+
encoding: "utf8",
|
|
19532
|
+
timeout: 5000,
|
|
19533
|
+
windowsHide: true,
|
|
19534
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" }
|
|
19535
|
+
});
|
|
19536
|
+
const [sha, committed, subject = "", trailer = ""] = (run.status === 0 ? run.stdout.trim() : "").split("\x1F");
|
|
19537
|
+
return sha && /^[0-9a-f]{40,64}$/.test(sha) ? { sha, committed: Number(committed), subject, trailer: trailer.trim() || null } : null;
|
|
19538
|
+
};
|
|
19539
|
+
var settle2 = (input, repo, file) => {
|
|
19540
|
+
let marker;
|
|
19541
|
+
try {
|
|
19542
|
+
marker = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
19543
|
+
} catch {
|
|
19544
|
+
return;
|
|
19545
|
+
}
|
|
19546
|
+
fs15.rmSync(file, { force: true });
|
|
19547
|
+
const session = input.session.id;
|
|
19548
|
+
const now = Date.now() / 1000;
|
|
19549
|
+
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
19550
|
+
return;
|
|
19551
|
+
if (!session || !repo.branch)
|
|
19552
|
+
return;
|
|
19553
|
+
const head = headCommit(repo.dir);
|
|
19554
|
+
if (!head || head.sha === marker.head || head.committed < marker.at)
|
|
19555
|
+
return;
|
|
19556
|
+
if (head.trailer && head.trailer !== session)
|
|
19557
|
+
return;
|
|
19558
|
+
const ledger = readLedger(repo.dir);
|
|
19559
|
+
if (!ledger.ok)
|
|
19560
|
+
return;
|
|
19561
|
+
const claimed = ledger.value.rows.some((row) => row.type === "commit.recorded" && (row.sha === head.sha || row.head === head.sha));
|
|
19562
|
+
if (claimed)
|
|
19563
|
+
return;
|
|
19564
|
+
appendHookObserved(repo.dir, {
|
|
19565
|
+
type: "commit.recorded",
|
|
19566
|
+
actor: { host: input.host, session, agentId: input.session.agentId },
|
|
19567
|
+
branch: repo.branch,
|
|
19568
|
+
head: head.sha,
|
|
19569
|
+
sha: head.sha,
|
|
19570
|
+
session,
|
|
19571
|
+
agentId: input.session.agentId,
|
|
19572
|
+
subject: head.subject.slice(0, 200),
|
|
19573
|
+
files: [],
|
|
19574
|
+
fileCount: null,
|
|
19575
|
+
via: "raw_shell"
|
|
19576
|
+
});
|
|
19577
|
+
};
|
|
19578
|
+
function rawGitPost(input, command, key) {
|
|
19579
|
+
try {
|
|
19580
|
+
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
19581
|
+
return;
|
|
19582
|
+
for (const { action, repo } of actionsIn(input.cwd, command, dialectOf(input)))
|
|
19583
|
+
if (action.kind === "commit")
|
|
19584
|
+
settle2(input, repo, markerFile(repo, input.session.id, key));
|
|
19585
|
+
} catch {}
|
|
19586
|
+
}
|
|
19587
|
+
function settlePendingCommit(input) {
|
|
19588
|
+
try {
|
|
19589
|
+
if (!input.session.id)
|
|
19590
|
+
return;
|
|
19591
|
+
const repo = repoAt(input.cwd);
|
|
19592
|
+
if (!repo)
|
|
19593
|
+
return;
|
|
19594
|
+
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
19595
|
+
if (fs15.existsSync(file))
|
|
19596
|
+
settle2(input, repo, file);
|
|
19597
|
+
} catch {}
|
|
19598
|
+
}
|
|
18887
19599
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18888
19600
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18889
19601
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
18890
|
-
var
|
|
19602
|
+
var SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
18891
19603
|
var WRITE_TOOLS = new Set([
|
|
18892
19604
|
"write",
|
|
18893
19605
|
"edit",
|
|
@@ -18920,7 +19632,7 @@ var writePaths = (input) => {
|
|
|
18920
19632
|
};
|
|
18921
19633
|
// packages/workit-core/src/hooks/handle.ts
|
|
18922
19634
|
var usable2 = (value) => value === "native" || value === "partial";
|
|
18923
|
-
var
|
|
19635
|
+
var NONE4 = { kind: "none" };
|
|
18924
19636
|
var offered = new Set;
|
|
18925
19637
|
var CLAUDE_WORKTREE_IMPLEMENTER = "workit:implementer";
|
|
18926
19638
|
var CLAUDE_JUDGES = new Set(["workit:verifier", "workit:reviewer"]);
|
|
@@ -18939,7 +19651,7 @@ function handleHook(input, deps) {
|
|
|
18939
19651
|
switch (event.kind) {
|
|
18940
19652
|
case "session.start": {
|
|
18941
19653
|
if (!usable2(descriptor.context.sessionStart))
|
|
18942
|
-
return
|
|
19654
|
+
return NONE4;
|
|
18943
19655
|
const key = input.session.id ? `${input.host}\x00${input.session.id}` : null;
|
|
18944
19656
|
const offer = event.source === "startup" && (key === null || !offered.has(key));
|
|
18945
19657
|
if (offer && key !== null)
|
|
@@ -18954,19 +19666,34 @@ function handleHook(input, deps) {
|
|
|
18954
19666
|
}
|
|
18955
19667
|
case "context.turn": {
|
|
18956
19668
|
if (!usable2(descriptor.context.perTurn))
|
|
18957
|
-
return
|
|
19669
|
+
return NONE4;
|
|
18958
19670
|
const text = turnContextText(input, descriptor);
|
|
18959
|
-
return text ? { kind: "context", text } :
|
|
19671
|
+
return text ? { kind: "context", text } : NONE4;
|
|
18960
19672
|
}
|
|
18961
19673
|
case "shell.pre": {
|
|
18962
|
-
const
|
|
18963
|
-
|
|
19674
|
+
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
19675
|
+
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
|
|
19676
|
+
if (policy.kind !== "none")
|
|
18964
19677
|
return policy;
|
|
18965
|
-
const
|
|
18966
|
-
|
|
19678
|
+
const postTool = usable2(descriptor.events["shell.post"].support);
|
|
19679
|
+
if (!postTool)
|
|
19680
|
+
settlePendingCommit(input);
|
|
19681
|
+
const raw = canDeny ? rawGitPre(input, event.command) : NONE4;
|
|
19682
|
+
if (raw.kind === "deny")
|
|
19683
|
+
return raw;
|
|
19684
|
+
const writes = usable2(descriptor.events["write.pre"].support) ? shellWrites(event.command) : { writes: false, targets: null };
|
|
19685
|
+
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE4;
|
|
19686
|
+
if (gate.kind === "deny")
|
|
19687
|
+
return gate;
|
|
19688
|
+
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
19689
|
+
return raw;
|
|
18967
19690
|
}
|
|
19691
|
+
case "shell.post":
|
|
19692
|
+
if (usable2(descriptor.events["shell.post"].support))
|
|
19693
|
+
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
19694
|
+
return NONE4;
|
|
18968
19695
|
case "write.pre":
|
|
18969
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) :
|
|
19696
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE4;
|
|
18970
19697
|
case "subagent.start":
|
|
18971
19698
|
return {
|
|
18972
19699
|
kind: "context",
|
|
@@ -18978,17 +19705,22 @@ function handleHook(input, deps) {
|
|
|
18978
19705
|
userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
|
|
18979
19706
|
};
|
|
18980
19707
|
case "tool.pre":
|
|
18981
|
-
case "shell.post":
|
|
18982
19708
|
case "subagent.stop":
|
|
18983
19709
|
case "prompt.submit":
|
|
18984
19710
|
case "stop":
|
|
18985
|
-
return
|
|
19711
|
+
return NONE4;
|
|
18986
19712
|
}
|
|
18987
19713
|
}
|
|
18988
19714
|
// packages/workit-core/src/hooks/hosts/claude-code.ts
|
|
18989
19715
|
var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
18990
19716
|
var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
|
|
18991
|
-
var CONTEXT_EVENTS = new Set([
|
|
19717
|
+
var CONTEXT_EVENTS = new Set([
|
|
19718
|
+
"SessionStart",
|
|
19719
|
+
"UserPromptSubmit",
|
|
19720
|
+
"SubagentStart",
|
|
19721
|
+
"PreToolUse",
|
|
19722
|
+
"PostToolUse"
|
|
19723
|
+
]);
|
|
18992
19724
|
|
|
18993
19725
|
// packages/workit-core/src/hooks/hosts/codex.ts
|
|
18994
19726
|
var SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
|
|
@@ -19190,21 +19922,30 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
19190
19922
|
}
|
|
19191
19923
|
});
|
|
19192
19924
|
};
|
|
19925
|
+
var hookInput = (ctx, event) => ({
|
|
19926
|
+
host: "pi",
|
|
19927
|
+
cwd: ctx.cwd,
|
|
19928
|
+
session: {
|
|
19929
|
+
id: process.env.WORKIT_PI_WORKER_SESSION || ctx.sessionManager?.getSessionId() || "",
|
|
19930
|
+
agentId: null,
|
|
19931
|
+
agentType: null,
|
|
19932
|
+
parentId: null
|
|
19933
|
+
},
|
|
19934
|
+
permissionMode: null,
|
|
19935
|
+
transcriptPath: null,
|
|
19936
|
+
event
|
|
19937
|
+
});
|
|
19193
19938
|
var enforceToolPolicy = (event, ctx) => {
|
|
19194
19939
|
const gate = (hookEvent) => {
|
|
19195
|
-
const decision = handleHook({
|
|
19196
|
-
|
|
19197
|
-
|
|
19198
|
-
|
|
19199
|
-
permissionMode: null,
|
|
19200
|
-
transcriptPath: null,
|
|
19201
|
-
event: hookEvent
|
|
19202
|
-
}, { descriptor: PI_DESCRIPTOR, addendum: null });
|
|
19940
|
+
const decision = handleHook(hookInput(ctx, hookEvent), {
|
|
19941
|
+
descriptor: PI_DESCRIPTOR,
|
|
19942
|
+
addendum: null
|
|
19943
|
+
});
|
|
19203
19944
|
return decision.kind === "deny" ? { block: true, reason: decision.reason } : undefined;
|
|
19204
19945
|
};
|
|
19205
19946
|
if (event.toolName === "bash") {
|
|
19206
19947
|
const command = event.input?.command;
|
|
19207
|
-
return typeof command === "string" ? gate({ kind: "shell.pre", command, toolUseId: null }) : undefined;
|
|
19948
|
+
return typeof command === "string" ? gate({ kind: "shell.pre", command, toolUseId: event.toolCallId ?? null }) : undefined;
|
|
19208
19949
|
}
|
|
19209
19950
|
if (event.toolName !== "write" && event.toolName !== "edit")
|
|
19210
19951
|
return;
|
|
@@ -19217,16 +19958,37 @@ var enforceToolPolicy = (event, ctx) => {
|
|
|
19217
19958
|
toolUseId: null
|
|
19218
19959
|
});
|
|
19219
19960
|
};
|
|
19961
|
+
var observeToolResult = (event, ctx) => {
|
|
19962
|
+
try {
|
|
19963
|
+
if (event.toolName !== "bash" || event.isError)
|
|
19964
|
+
return;
|
|
19965
|
+
const command = event.input?.command;
|
|
19966
|
+
if (typeof command !== "string")
|
|
19967
|
+
return;
|
|
19968
|
+
const post = hookInput(ctx, {
|
|
19969
|
+
kind: "shell.post",
|
|
19970
|
+
command,
|
|
19971
|
+
stdout: "",
|
|
19972
|
+
exitCode: null,
|
|
19973
|
+
toolUseId: event.toolCallId
|
|
19974
|
+
});
|
|
19975
|
+
handleHook(post, { descriptor: PI_DESCRIPTOR, addendum: null });
|
|
19976
|
+
const nudge = rawGitPre(post, command);
|
|
19977
|
+
return nudge.kind === "context" ? { content: [...event.content, { type: "text", text: nudge.text }] } : undefined;
|
|
19978
|
+
} catch {
|
|
19979
|
+
return;
|
|
19980
|
+
}
|
|
19981
|
+
};
|
|
19220
19982
|
|
|
19221
19983
|
// packages/workit-pi/src/launches.ts
|
|
19222
19984
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
19223
|
-
import
|
|
19224
|
-
import
|
|
19985
|
+
import os5 from "node:os";
|
|
19986
|
+
import path23 from "node:path";
|
|
19225
19987
|
var launchDir = (root) => {
|
|
19226
19988
|
const location = resolveStore(root);
|
|
19227
|
-
return location instanceof Error ? null :
|
|
19989
|
+
return location instanceof Error ? null : path23.join(location.dir, "pi-launches");
|
|
19228
19990
|
};
|
|
19229
|
-
var fileFor = (dir, workerId) =>
|
|
19991
|
+
var fileFor = (dir, workerId) => path23.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
|
|
19230
19992
|
var recordLaunch = (root, attempt) => {
|
|
19231
19993
|
const dir = launchDir(root);
|
|
19232
19994
|
if (!dir)
|
|
@@ -19236,7 +19998,7 @@ var recordLaunch = (root, attempt) => {
|
|
|
19236
19998
|
writeFileSync3(fileFor(dir, attempt.workerId), JSON.stringify({
|
|
19237
19999
|
...attempt,
|
|
19238
20000
|
pid: process.pid,
|
|
19239
|
-
host:
|
|
20001
|
+
host: os5.hostname(),
|
|
19240
20002
|
at: new Date().toISOString()
|
|
19241
20003
|
}), { flag: "wx" });
|
|
19242
20004
|
return true;
|
|
@@ -19250,7 +20012,7 @@ var clearLaunch = (root, workerId) => {
|
|
|
19250
20012
|
rmSync2(fileFor(dir, workerId), { force: true });
|
|
19251
20013
|
};
|
|
19252
20014
|
var alive = (attempt) => {
|
|
19253
|
-
if (attempt.host !==
|
|
20015
|
+
if (attempt.host !== os5.hostname())
|
|
19254
20016
|
return true;
|
|
19255
20017
|
try {
|
|
19256
20018
|
process.kill(attempt.pid, 0);
|
|
@@ -19272,7 +20034,7 @@ var abandonedLaunches = (root) => {
|
|
|
19272
20034
|
const found = [];
|
|
19273
20035
|
for (const name of names) {
|
|
19274
20036
|
try {
|
|
19275
|
-
const attempt = JSON.parse(readFileSync6(
|
|
20037
|
+
const attempt = JSON.parse(readFileSync6(path23.join(dir, name), "utf8"));
|
|
19276
20038
|
if (typeof attempt.workerId === "string" && typeof attempt.session === "string") {
|
|
19277
20039
|
if (!alive(attempt))
|
|
19278
20040
|
found.push(attempt);
|
|
@@ -19445,6 +20207,8 @@ function launchWorker(assignment, options) {
|
|
|
19445
20207
|
WORKIT_PI_TASK_ID: taskId ?? "",
|
|
19446
20208
|
WORKIT_PI_WORKER_ID: workerId ?? "",
|
|
19447
20209
|
WORKIT_PI_WORKER_SESSION: sessionId,
|
|
20210
|
+
WORKIT_HOST: "pi",
|
|
20211
|
+
WORKIT_SESSION_ID: sessionId,
|
|
19448
20212
|
WORKIT_PI_WORKER_ROLE: assignment.role,
|
|
19449
20213
|
WORKIT_PI_WORKER_SCOPE: JSON.stringify(assignment.scope)
|
|
19450
20214
|
};
|
|
@@ -20138,6 +20902,7 @@ function extension(pi) {
|
|
|
20138
20902
|
toolCallId: event.toolCallId,
|
|
20139
20903
|
isError: event.isError
|
|
20140
20904
|
});
|
|
20905
|
+
return observeToolResult(event, ctx);
|
|
20141
20906
|
});
|
|
20142
20907
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
20143
20908
|
sessions.delete(ctx.sessionManager.getSessionId());
|