@brainervirus/workit-cursor 8.4.1 → 8.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +468 -77
- package/dist/mcp-server.js +40 -6
- package/dist/workit-hook.js +480 -75
- package/package.json +3 -3
- package/skills/workit-retro/SKILL.md +2 -2
package/dist/workit-hook.js
CHANGED
|
@@ -17273,6 +17273,8 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
17273
17273
|
};
|
|
17274
17274
|
});
|
|
17275
17275
|
// packages/workit-core/src/hooks/context.ts
|
|
17276
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
17277
|
+
import fs13 from "node:fs";
|
|
17276
17278
|
import path16 from "node:path";
|
|
17277
17279
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
17278
17280
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
@@ -17420,8 +17422,77 @@ var turnContextText = (input, descriptor) => {
|
|
|
17420
17422
|
return null;
|
|
17421
17423
|
}
|
|
17422
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
|
+
};
|
|
17423
17494
|
// packages/workit-core/src/core/branch.ts
|
|
17424
|
-
import { createHash as
|
|
17495
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
17425
17496
|
var withReleaseTracks = (workspaceRoot, policy) => {
|
|
17426
17497
|
const read = workspaceReleaseTracks(workspaceRoot);
|
|
17427
17498
|
if (read.error)
|
|
@@ -17489,7 +17560,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
17489
17560
|
const source = resolved.provenance.branchPolicy;
|
|
17490
17561
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
17491
17562
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
17492
|
-
const fingerprint =
|
|
17563
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
17493
17564
|
return {
|
|
17494
17565
|
branchPolicy,
|
|
17495
17566
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -17824,7 +17895,7 @@ function writeGate(cwd, paths) {
|
|
|
17824
17895
|
const gate = gateFor(cwd);
|
|
17825
17896
|
if (!gate || gate.blockers.length === 0)
|
|
17826
17897
|
return NONE2;
|
|
17827
|
-
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)))
|
|
17828
17899
|
return NONE2;
|
|
17829
17900
|
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
17830
17901
|
return {
|
|
@@ -17838,8 +17909,8 @@ function writeGate(cwd, paths) {
|
|
|
17838
17909
|
}
|
|
17839
17910
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
17840
17911
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17841
|
-
import { createHash as
|
|
17842
|
-
import
|
|
17912
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
17913
|
+
import fs14 from "node:fs";
|
|
17843
17914
|
import os4 from "node:os";
|
|
17844
17915
|
import path18 from "node:path";
|
|
17845
17916
|
var NONE3 = { kind: "none" };
|
|
@@ -18085,7 +18156,7 @@ var repoAt = (dir) => {
|
|
|
18085
18156
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
18086
18157
|
};
|
|
18087
18158
|
var isWorkitWorkspace = (repo) => {
|
|
18088
|
-
if (
|
|
18159
|
+
if (fs14.existsSync(repo.location.dir))
|
|
18089
18160
|
return true;
|
|
18090
18161
|
try {
|
|
18091
18162
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -18116,7 +18187,7 @@ var headSha2 = (repo) => {
|
|
|
18116
18187
|
if (!git || git.reftable)
|
|
18117
18188
|
return null;
|
|
18118
18189
|
try {
|
|
18119
|
-
const head =
|
|
18190
|
+
const head = fs14.readFileSync(path18.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18120
18191
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18121
18192
|
return head;
|
|
18122
18193
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
@@ -18124,9 +18195,9 @@ var headSha2 = (repo) => {
|
|
|
18124
18195
|
return null;
|
|
18125
18196
|
const common = path18.dirname(repo.location.dir);
|
|
18126
18197
|
try {
|
|
18127
|
-
return
|
|
18198
|
+
return fs14.readFileSync(path18.join(common, ref), "utf8").trim() || null;
|
|
18128
18199
|
} catch {}
|
|
18129
|
-
const packed =
|
|
18200
|
+
const packed = fs14.readFileSync(path18.join(common, "packed-refs"), "utf8");
|
|
18130
18201
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18131
18202
|
} catch {
|
|
18132
18203
|
return null;
|
|
@@ -18174,6 +18245,11 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
18174
18245
|
}
|
|
18175
18246
|
return out;
|
|
18176
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");
|
|
18177
18253
|
var mentionsRawTool = (command) => /\b(?:git|gh|glab)\b/i.test(command);
|
|
18178
18254
|
var dialectOf = (input) => (input.event.kind === "shell.pre" || input.event.kind === "shell.post" ? input.event.dialect : undefined) ?? "posix";
|
|
18179
18255
|
function rawGitPre(input, command) {
|
|
@@ -18203,8 +18279,8 @@ function rawGitPre(input, command) {
|
|
|
18203
18279
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
18204
18280
|
var NEXT_COMMAND = "next";
|
|
18205
18281
|
var MARKER_TTL_SECONDS = 3600;
|
|
18206
|
-
var markerFile = (repo, session, key) => path18.join(repo.location.dir, "hooks", `commit-${
|
|
18207
|
-
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")}`;
|
|
18208
18284
|
function noteRawCommit(input, command, key) {
|
|
18209
18285
|
try {
|
|
18210
18286
|
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
@@ -18214,8 +18290,8 @@ function noteRawCommit(input, command, key) {
|
|
|
18214
18290
|
if (action.kind !== "commit")
|
|
18215
18291
|
continue;
|
|
18216
18292
|
const file = markerFile(repo, input.session.id, key);
|
|
18217
|
-
|
|
18218
|
-
|
|
18293
|
+
fs14.mkdirSync(path18.dirname(file), { recursive: true });
|
|
18294
|
+
fs14.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18219
18295
|
`);
|
|
18220
18296
|
}
|
|
18221
18297
|
} catch {}
|
|
@@ -18238,11 +18314,11 @@ var headCommit = (dir) => {
|
|
|
18238
18314
|
var settle2 = (input, repo, file) => {
|
|
18239
18315
|
let marker;
|
|
18240
18316
|
try {
|
|
18241
|
-
marker = JSON.parse(
|
|
18317
|
+
marker = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
18242
18318
|
} catch {
|
|
18243
18319
|
return;
|
|
18244
18320
|
}
|
|
18245
|
-
|
|
18321
|
+
fs14.rmSync(file, { force: true });
|
|
18246
18322
|
const session = input.session.id;
|
|
18247
18323
|
const now = Date.now() / 1000;
|
|
18248
18324
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -18291,18 +18367,297 @@ function settlePendingCommit(input) {
|
|
|
18291
18367
|
if (!repo)
|
|
18292
18368
|
return;
|
|
18293
18369
|
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
18294
|
-
if (
|
|
18370
|
+
if (fs14.existsSync(file))
|
|
18295
18371
|
settle2(input, repo, file);
|
|
18296
18372
|
} catch {}
|
|
18297
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|regressed|(?:is|introduced) a regression|regression (?:in|after|since|from)|(?: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)(?=\s*$|\s+(?:for|to|on|with|and|against|into|from|after|once)\b|[.!,;])|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
|
+
[
|
|
18453
|
+
"workit-fanout",
|
|
18454
|
+
/\b(fan(?: |-)?out|parallelize|parallel agents|agent swarm|swarm of agents)\b/i
|
|
18455
|
+
],
|
|
18456
|
+
["workit-verify-app", /\b(verify the app|smoke test|prove it works)\b/i],
|
|
18457
|
+
["workit-retro", /\b((?:a|the) retro(?![-\w]|\.\w)|retrospective)/i],
|
|
18458
|
+
["workit-architecture", /\b(deepen (?:the )?modules|architecture (?:review|pass|audit))\b/i],
|
|
18459
|
+
[
|
|
18460
|
+
"workit-shape",
|
|
18461
|
+
/\b(brainstorm\w*|grill me|should we|let'?s plan|plan (?:out|how|the|a|for)|(?:write|draft) (?:a|the) spec)\b/i
|
|
18462
|
+
],
|
|
18463
|
+
[
|
|
18464
|
+
"workit-implement",
|
|
18465
|
+
/\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
|
|
18466
|
+
]
|
|
18467
|
+
];
|
|
18468
|
+
var workitSkillOf = (name) => {
|
|
18469
|
+
const bare = name.trim().replace(/^\//, "").toLowerCase();
|
|
18470
|
+
const alias = WORKIT_SKILL_ALIASES[bare];
|
|
18471
|
+
if (alias)
|
|
18472
|
+
return alias;
|
|
18473
|
+
const skill = bare.replace(/^workit:/, "workit-");
|
|
18474
|
+
return WORKIT_METHOD_SKILLS.includes(skill) ? skill : null;
|
|
18475
|
+
};
|
|
18476
|
+
var promptTrigger = (prompt) => {
|
|
18477
|
+
const text = prompt.replace(/```[\s\S]*?```/g, " ").slice(0, 4000);
|
|
18478
|
+
for (const [skill, pattern] of PROMPT_INTENTS) {
|
|
18479
|
+
const match = pattern.exec(text);
|
|
18480
|
+
if (match)
|
|
18481
|
+
return { skill, trigger: match[1] ?? match[0] };
|
|
18482
|
+
}
|
|
18483
|
+
return null;
|
|
18484
|
+
};
|
|
18485
|
+
var markerFile2 = (input, session) => {
|
|
18486
|
+
const location = resolveStore(input.cwd);
|
|
18487
|
+
if (location instanceof Error || !location.shared)
|
|
18488
|
+
return null;
|
|
18489
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18490
|
+
return path19.join(location.dir, "hooks", `skills-${name}.json`);
|
|
18491
|
+
};
|
|
18492
|
+
var readMarker = (file) => {
|
|
18493
|
+
try {
|
|
18494
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
18495
|
+
return { nudged: value.nudged ?? [], loaded: value.loaded ?? [] };
|
|
18496
|
+
} catch {
|
|
18497
|
+
return { nudged: [], loaded: [] };
|
|
18498
|
+
}
|
|
18499
|
+
};
|
|
18500
|
+
var writeMarker = (file, marker) => {
|
|
18501
|
+
try {
|
|
18502
|
+
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18503
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
18504
|
+
} catch {}
|
|
18505
|
+
};
|
|
18506
|
+
var loadedSkills = (cwd, session) => {
|
|
18507
|
+
const ledger = readLedger(cwd);
|
|
18508
|
+
const loaded = new Set;
|
|
18509
|
+
if (!ledger.ok)
|
|
18510
|
+
return loaded;
|
|
18511
|
+
for (const row of ledger.value.rows)
|
|
18512
|
+
if (row.type === "skill.loaded" && row.actor.session === session && typeof row.skill === "string")
|
|
18513
|
+
loaded.add(row.skill);
|
|
18514
|
+
return loaded;
|
|
18515
|
+
};
|
|
18516
|
+
var SKILL_FILE = /(?:^|[\\/])(workit-[a-z-]+)[\\/]SKILL\.md$/;
|
|
18517
|
+
var skillFileIn = (file) => SKILL_FILE.exec(file)?.[1] ?? null;
|
|
18518
|
+
var READERS = new Set([
|
|
18519
|
+
"cat",
|
|
18520
|
+
"sed",
|
|
18521
|
+
"head",
|
|
18522
|
+
"tail",
|
|
18523
|
+
"less",
|
|
18524
|
+
"more",
|
|
18525
|
+
"bat",
|
|
18526
|
+
"nl",
|
|
18527
|
+
"get-content",
|
|
18528
|
+
"gc",
|
|
18529
|
+
"type"
|
|
18530
|
+
]);
|
|
18531
|
+
var skillReadIn = (command, dialect = "posix") => {
|
|
18532
|
+
for (const { words } of segmentsOf(command, dialect)) {
|
|
18533
|
+
if (!READERS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
|
|
18534
|
+
continue;
|
|
18535
|
+
if (words.some((word) => /^(?:-i(?:\.\w+)?|--in-place(?:=.*)?)$/.test(word)))
|
|
18536
|
+
continue;
|
|
18537
|
+
for (const word of words.slice(1)) {
|
|
18538
|
+
const skill = skillFileIn(word);
|
|
18539
|
+
if (skill)
|
|
18540
|
+
return skill;
|
|
18541
|
+
}
|
|
18542
|
+
}
|
|
18543
|
+
return null;
|
|
18544
|
+
};
|
|
18545
|
+
function recordSkillLoad(input, name, via) {
|
|
18546
|
+
try {
|
|
18547
|
+
const skill = workitSkillOf(name);
|
|
18548
|
+
const session = input.session.id;
|
|
18549
|
+
if (!skill || !session)
|
|
18550
|
+
return;
|
|
18551
|
+
if (!inWorkitWorkspace(input.cwd))
|
|
18552
|
+
return;
|
|
18553
|
+
const file = markerFile2(input, session);
|
|
18554
|
+
if (!file)
|
|
18555
|
+
return;
|
|
18556
|
+
const marker = readMarker(file);
|
|
18557
|
+
if (marker.loaded.includes(skill))
|
|
18558
|
+
return;
|
|
18559
|
+
writeMarker(file, { ...marker, loaded: [...marker.loaded, skill] });
|
|
18560
|
+
if (loadedSkills(input.cwd, session).has(skill))
|
|
18561
|
+
return;
|
|
18562
|
+
appendHookObserved(input.cwd, {
|
|
18563
|
+
type: "skill.loaded",
|
|
18564
|
+
actor: { host: input.host, session, agentId: input.session.agentId },
|
|
18565
|
+
skill,
|
|
18566
|
+
via
|
|
18567
|
+
});
|
|
18568
|
+
} catch {}
|
|
18569
|
+
}
|
|
18570
|
+
var nudgeOnce = (input, session, skill, line) => {
|
|
18571
|
+
const file = markerFile2(input, session);
|
|
18572
|
+
if (!file)
|
|
18573
|
+
return null;
|
|
18574
|
+
const marker = readMarker(file);
|
|
18575
|
+
if (marker.nudged.includes(skill) || marker.loaded.includes(skill))
|
|
18576
|
+
return null;
|
|
18577
|
+
if (loadedSkills(input.cwd, session).has(skill))
|
|
18578
|
+
return null;
|
|
18579
|
+
writeMarker(file, { ...marker, nudged: [...marker.nudged, skill] });
|
|
18580
|
+
return line;
|
|
18581
|
+
};
|
|
18582
|
+
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.`;
|
|
18583
|
+
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;
|
|
18584
|
+
function promptNudge(input, prompt) {
|
|
18585
|
+
try {
|
|
18586
|
+
const session = input.session.id;
|
|
18587
|
+
if (!prompt || !session || input.session.agentId)
|
|
18588
|
+
return null;
|
|
18589
|
+
const loads = promptLoads(prompt);
|
|
18590
|
+
if (loads) {
|
|
18591
|
+
recordSkillLoad(input, loads, "slash");
|
|
18592
|
+
return null;
|
|
18593
|
+
}
|
|
18594
|
+
const hit = promptTrigger(prompt);
|
|
18595
|
+
if (!hit || !inWorkitWorkspace(input.cwd))
|
|
18596
|
+
return null;
|
|
18597
|
+
return nudgeOnce(input, session, hit.skill, nudgeText(input, hit.skill, `this request ("${hit.trigger}")`));
|
|
18598
|
+
} catch {
|
|
18599
|
+
return null;
|
|
18600
|
+
}
|
|
18601
|
+
}
|
|
18602
|
+
var WORKIT_DELIVERY = /^(?:pr (?:create|merge)|ci wait|git push)\b/;
|
|
18603
|
+
var SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
18604
|
+
var LAUNCHERS = new Set([
|
|
18605
|
+
"npx",
|
|
18606
|
+
"bunx",
|
|
18607
|
+
"pnpx",
|
|
18608
|
+
"-y",
|
|
18609
|
+
"--yes",
|
|
18610
|
+
"pnpm",
|
|
18611
|
+
"dlx",
|
|
18612
|
+
"exec",
|
|
18613
|
+
"env",
|
|
18614
|
+
"sudo",
|
|
18615
|
+
"command"
|
|
18616
|
+
]);
|
|
18617
|
+
var runsWorkitDelivery = (command, dialect, depth = 0) => segmentsOf(command, dialect).some(({ words }) => {
|
|
18618
|
+
const at = words.findIndex((word) => !/^\w+=/.test(word) && !LAUNCHERS.has(word));
|
|
18619
|
+
if (at >= 0 && /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/.test(words[at])) {
|
|
18620
|
+
const args = words.slice(at + 1);
|
|
18621
|
+
if (args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run"))
|
|
18622
|
+
return false;
|
|
18623
|
+
return WORKIT_DELIVERY.test(args.join(" "));
|
|
18624
|
+
}
|
|
18625
|
+
const shell = SHELLS2.has(words[at]?.split(/[\\/]/).at(-1) ?? "");
|
|
18626
|
+
const script = shell && words[at + 1] === "-c" ? words[at + 2] : undefined;
|
|
18627
|
+
return depth < 3 && script !== undefined && runsWorkitDelivery(script, "posix", depth + 1);
|
|
18628
|
+
});
|
|
18629
|
+
var isDeliveryCommand = (cwd, command, dialect = "posix") => rawDelivery(cwd, command, dialect) || runsWorkitDelivery(command, dialect) && inWorkitWorkspace(cwd);
|
|
18630
|
+
function shipNudge(input, command) {
|
|
18631
|
+
try {
|
|
18632
|
+
const session = input.session.id;
|
|
18633
|
+
if (!session || input.session.agentId)
|
|
18634
|
+
return null;
|
|
18635
|
+
const dialect = input.event.kind === "shell.pre" ? input.event.dialect ?? "posix" : "posix";
|
|
18636
|
+
if (!isDeliveryCommand(input.cwd, command, dialect))
|
|
18637
|
+
return null;
|
|
18638
|
+
return nudgeOnce(input, session, "workit-ship", nudgeText(input, "workit-ship", "this delivery step"));
|
|
18639
|
+
} catch {
|
|
18640
|
+
return null;
|
|
18641
|
+
}
|
|
18642
|
+
}
|
|
18643
|
+
var withContextLine = (decision, line) => {
|
|
18644
|
+
if (!line)
|
|
18645
|
+
return decision;
|
|
18646
|
+
if (decision.kind === "none")
|
|
18647
|
+
return { kind: "context", text: line };
|
|
18648
|
+
if (decision.kind === "context")
|
|
18649
|
+
return { kind: "context", text: `${decision.text}
|
|
18650
|
+
${line}` };
|
|
18651
|
+
return decision;
|
|
18652
|
+
};
|
|
18298
18653
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18299
18654
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
18300
|
-
import
|
|
18655
|
+
import path20 from "node:path";
|
|
18301
18656
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18302
18657
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
18303
18658
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
18304
18659
|
var existingDirectory = (value) => {
|
|
18305
|
-
if (!nonEmpty2(value) || !
|
|
18660
|
+
if (!nonEmpty2(value) || !path20.isAbsolute(value) || !existsSync6(value))
|
|
18306
18661
|
return null;
|
|
18307
18662
|
try {
|
|
18308
18663
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -18310,7 +18665,7 @@ var existingDirectory = (value) => {
|
|
|
18310
18665
|
return null;
|
|
18311
18666
|
}
|
|
18312
18667
|
};
|
|
18313
|
-
var
|
|
18668
|
+
var SHELLS3 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
18314
18669
|
var SCRIPT_FLAG2 = /^(?:-\w*c|-command|\/c)$/i;
|
|
18315
18670
|
var quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg);
|
|
18316
18671
|
var commandText = (value) => {
|
|
@@ -18322,7 +18677,7 @@ var commandText = (value) => {
|
|
|
18322
18677
|
if (!argv.every((arg) => typeof arg === "string"))
|
|
18323
18678
|
return null;
|
|
18324
18679
|
const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
|
|
18325
|
-
if (
|
|
18680
|
+
if (SHELLS3.has(shell))
|
|
18326
18681
|
for (let index = 1;index < argv.length - 1; index++) {
|
|
18327
18682
|
if (SCRIPT_FLAG2.test(argv[index]))
|
|
18328
18683
|
return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
|
|
@@ -18353,30 +18708,49 @@ var WRITE_TOOLS = new Set([
|
|
|
18353
18708
|
]);
|
|
18354
18709
|
var isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, ""));
|
|
18355
18710
|
var PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
|
|
18711
|
+
var PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
|
|
18356
18712
|
var writePaths = (input) => {
|
|
18357
18713
|
if (!isRecord4(input))
|
|
18358
18714
|
return [];
|
|
18359
18715
|
const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
|
|
18360
18716
|
for (const value of Object.values(input))
|
|
18361
18717
|
if (typeof value === "string")
|
|
18362
|
-
for (const match of value.matchAll(
|
|
18718
|
+
for (const match of value.matchAll(PATCH_HEADER))
|
|
18363
18719
|
paths.push(match[1].trim());
|
|
18364
18720
|
return [...new Set(paths)];
|
|
18365
18721
|
};
|
|
18722
|
+
var writeTargets = (input) => {
|
|
18723
|
+
const paths = writePaths(input);
|
|
18724
|
+
return paths.length > 0 ? paths : null;
|
|
18725
|
+
};
|
|
18366
18726
|
// packages/workit-core/src/hooks/handle.ts
|
|
18367
18727
|
var usable2 = (value) => value === "native" || value === "partial";
|
|
18368
18728
|
var NONE4 = { kind: "none" };
|
|
18729
|
+
var resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn);
|
|
18369
18730
|
var offered = new Set;
|
|
18370
|
-
var
|
|
18371
|
-
|
|
18731
|
+
var ROLES = {
|
|
18732
|
+
implementer: "implementer",
|
|
18733
|
+
verifier: "judge",
|
|
18734
|
+
reviewer: "judge"
|
|
18735
|
+
};
|
|
18736
|
+
var workitRole = (descriptor, agentType) => {
|
|
18737
|
+
const prefix = descriptor.subagents.agentPrefix;
|
|
18738
|
+
const name = agentType.startsWith(prefix) ? agentType.slice(prefix.length) : "";
|
|
18739
|
+
return Object.hasOwn(ROLES, name) ? ROLES[name] : null;
|
|
18740
|
+
};
|
|
18372
18741
|
var subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128);
|
|
18373
|
-
var subagentStartText = (
|
|
18374
|
-
|
|
18375
|
-
|
|
18376
|
-
|
|
18377
|
-
|
|
18378
|
-
return
|
|
18379
|
-
|
|
18742
|
+
var subagentStartText = (session, descriptor, event) => {
|
|
18743
|
+
const role = workitRole(descriptor, event.agentType);
|
|
18744
|
+
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
18745
|
+
if (role === "implementer") {
|
|
18746
|
+
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";
|
|
18747
|
+
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.`;
|
|
18748
|
+
}
|
|
18749
|
+
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
18750
|
+
if (role !== "judge" || !event.agentId)
|
|
18751
|
+
return readOnly;
|
|
18752
|
+
const own = subagentSession(session, event.agentId);
|
|
18753
|
+
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.`;
|
|
18380
18754
|
};
|
|
18381
18755
|
function handleHook(input, deps) {
|
|
18382
18756
|
const { descriptor } = deps;
|
|
@@ -18392,18 +18766,22 @@ function handleHook(input, deps) {
|
|
|
18392
18766
|
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.`;
|
|
18393
18767
|
const addendum = [deps.addendum, advisory].filter(Boolean).join(`
|
|
18394
18768
|
`) || null;
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
};
|
|
18769
|
+
const text = sessionContextText(input, descriptor, { offer, addendum });
|
|
18770
|
+
if (resendsOnChange(descriptor))
|
|
18771
|
+
seedTurnContext(input, descriptor);
|
|
18772
|
+
return { kind: "context", text };
|
|
18399
18773
|
}
|
|
18400
18774
|
case "context.turn": {
|
|
18401
18775
|
if (!usable2(descriptor.context.perTurn))
|
|
18402
18776
|
return NONE4;
|
|
18403
|
-
const
|
|
18404
|
-
|
|
18777
|
+
const current = turnContextText(input, descriptor);
|
|
18778
|
+
const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
|
|
18779
|
+
return withContextLine(text ? { kind: "context", text } : NONE4, promptNudge(input, event.prompt));
|
|
18405
18780
|
}
|
|
18406
18781
|
case "shell.pre": {
|
|
18782
|
+
const skillRead = skillReadIn(event.command, event.dialect);
|
|
18783
|
+
if (skillRead)
|
|
18784
|
+
recordSkillLoad(input, skillRead, "read");
|
|
18407
18785
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
18408
18786
|
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
|
|
18409
18787
|
if (policy.kind !== "none")
|
|
@@ -18419,7 +18797,7 @@ function handleHook(input, deps) {
|
|
|
18419
18797
|
if (gate.kind === "deny")
|
|
18420
18798
|
return gate;
|
|
18421
18799
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
18422
|
-
return raw;
|
|
18800
|
+
return withContextLine(raw, shipNudge(input, event.command));
|
|
18423
18801
|
}
|
|
18424
18802
|
case "shell.post":
|
|
18425
18803
|
if (usable2(descriptor.events["shell.post"].support))
|
|
@@ -18430,7 +18808,7 @@ function handleHook(input, deps) {
|
|
|
18430
18808
|
case "subagent.start":
|
|
18431
18809
|
return {
|
|
18432
18810
|
kind: "context",
|
|
18433
|
-
text: subagentStartText(input.
|
|
18811
|
+
text: subagentStartText(input.session.id ?? null, descriptor, event)
|
|
18434
18812
|
};
|
|
18435
18813
|
case "compact.pre":
|
|
18436
18814
|
return {
|
|
@@ -18438,6 +18816,9 @@ function handleHook(input, deps) {
|
|
|
18438
18816
|
userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
|
|
18439
18817
|
};
|
|
18440
18818
|
case "tool.pre":
|
|
18819
|
+
if (event.skill)
|
|
18820
|
+
recordSkillLoad(input, event.skill, "tool");
|
|
18821
|
+
return NONE4;
|
|
18441
18822
|
case "subagent.stop":
|
|
18442
18823
|
case "prompt.submit":
|
|
18443
18824
|
case "stop":
|
|
@@ -18488,24 +18869,26 @@ var CLAUDE_CODE_DESCRIPTOR = {
|
|
|
18488
18869
|
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18489
18870
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18490
18871
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18491
|
-
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18492
18872
|
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18493
|
-
"
|
|
18494
|
-
|
|
18873
|
+
"subagent.stop": { support: "none", native: null },
|
|
18874
|
+
"compact.pre": { support: "none", native: null },
|
|
18875
|
+
stop: { support: "none", native: null }
|
|
18495
18876
|
},
|
|
18496
18877
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18497
18878
|
context: {
|
|
18498
18879
|
sessionStart: "native",
|
|
18499
18880
|
perTurn: "native",
|
|
18500
18881
|
afterCompact: "native",
|
|
18501
|
-
task: "session-bound"
|
|
18882
|
+
task: "session-bound",
|
|
18883
|
+
turnResend: "every-turn"
|
|
18502
18884
|
},
|
|
18503
18885
|
subagents: {
|
|
18504
18886
|
identity: "native",
|
|
18505
18887
|
parentBinding: "partial",
|
|
18506
18888
|
blockStart: "none",
|
|
18507
18889
|
worktreeIsolation: "native",
|
|
18508
|
-
maxConcurrency: "undocumented"
|
|
18890
|
+
maxConcurrency: "undocumented",
|
|
18891
|
+
agentPrefix: "workit:"
|
|
18509
18892
|
},
|
|
18510
18893
|
provenance: { sessionId: "native", agentIdOnTool: "native", postToolObserve: "native" },
|
|
18511
18894
|
interaction: { questions: "undocumented", writeBoundary: "partial" },
|
|
@@ -18523,13 +18906,13 @@ var CLAUDE_CODE_DESCRIPTOR = {
|
|
|
18523
18906
|
},
|
|
18524
18907
|
{
|
|
18525
18908
|
name: "native_subagents",
|
|
18526
|
-
surface: "SubagentStart
|
|
18527
|
-
refs: ["SubagentStart"
|
|
18528
|
-
requires: ["event:subagent.start"
|
|
18529
|
-
observed: ["subagent.start"
|
|
18909
|
+
surface: "SubagentStart",
|
|
18910
|
+
refs: ["SubagentStart"],
|
|
18911
|
+
requires: ["event:subagent.start"],
|
|
18912
|
+
observed: ["subagent.start"],
|
|
18530
18913
|
assurance: "agent_guided",
|
|
18531
18914
|
reason: "Claude Code reports stable agent identities, but SubagentStart cannot block or bind",
|
|
18532
|
-
unavailableReason: "Claude Code
|
|
18915
|
+
unavailableReason: "Claude Code SubagentStart is unavailable"
|
|
18533
18916
|
},
|
|
18534
18917
|
{
|
|
18535
18918
|
name: "fresh-context-review",
|
|
@@ -18580,7 +18963,7 @@ var eventOf = (name, value) => {
|
|
|
18580
18963
|
case "SessionStart":
|
|
18581
18964
|
return SOURCES.has(value.source) ? { ok: true, event: { kind: "session.start", source: value.source } } : { ok: false, error: "SessionStart source is required" };
|
|
18582
18965
|
case "UserPromptSubmit":
|
|
18583
|
-
return { ok: true, event: { kind: "context.turn" } };
|
|
18966
|
+
return { ok: true, event: { kind: "context.turn", prompt: optionalText(value.prompt) } };
|
|
18584
18967
|
case "PreToolUse": {
|
|
18585
18968
|
if (!nonEmpty2(value.tool_name))
|
|
18586
18969
|
return { ok: false, error: "tool_name is required" };
|
|
@@ -18590,10 +18973,20 @@ var eventOf = (name, value) => {
|
|
|
18590
18973
|
event: {
|
|
18591
18974
|
kind: "write.pre",
|
|
18592
18975
|
tool: value.tool_name,
|
|
18593
|
-
paths:
|
|
18976
|
+
paths: writeTargets(value.tool_input),
|
|
18594
18977
|
toolUseId
|
|
18595
18978
|
}
|
|
18596
18979
|
};
|
|
18980
|
+
if (value.tool_name === "Skill")
|
|
18981
|
+
return {
|
|
18982
|
+
ok: true,
|
|
18983
|
+
event: {
|
|
18984
|
+
kind: "tool.pre",
|
|
18985
|
+
tool: value.tool_name,
|
|
18986
|
+
toolUseId,
|
|
18987
|
+
skill: isRecord4(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
18988
|
+
}
|
|
18989
|
+
};
|
|
18597
18990
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
18598
18991
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
18599
18992
|
const command = toolCommand(value);
|
|
@@ -18728,39 +19121,40 @@ var claudeCodeAdapter = {
|
|
|
18728
19121
|
function detectCodexSurface(env) {
|
|
18729
19122
|
return env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || Boolean(env.CODEX_ELECTRON_RESOURCES_PATH) ? "codex_desktop" : "codex_cli";
|
|
18730
19123
|
}
|
|
18731
|
-
var undocumented = { support: "undocumented", native: null };
|
|
18732
19124
|
var CODEX_DESCRIPTOR = {
|
|
18733
19125
|
host: "codex_cli",
|
|
18734
19126
|
label: "Codex",
|
|
18735
|
-
verifiedAgainst: "codex-cli 0.
|
|
19127
|
+
verifiedAgainst: "codex-cli 0.160.1 (hook JSON schemas in the binary); Codex Desktop 26.901.20858",
|
|
18736
19128
|
docs: ["https://developers.openai.com/codex/hooks"],
|
|
18737
19129
|
transport: "hook-process",
|
|
18738
19130
|
events: {
|
|
18739
19131
|
"session.start": { support: "native", native: "SessionStart" },
|
|
18740
|
-
"context.turn":
|
|
19132
|
+
"context.turn": { support: "native", native: "UserPromptSubmit" },
|
|
18741
19133
|
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
18742
19134
|
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
18743
|
-
"write.pre": { support: "
|
|
19135
|
+
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18744
19136
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18745
19137
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18746
19138
|
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18747
|
-
"prompt.submit":
|
|
19139
|
+
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18748
19140
|
"compact.pre": { support: "none", native: null },
|
|
18749
|
-
stop:
|
|
19141
|
+
stop: { support: "none", native: null }
|
|
18750
19142
|
},
|
|
18751
19143
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18752
19144
|
context: {
|
|
18753
19145
|
sessionStart: "native",
|
|
18754
|
-
perTurn: "
|
|
19146
|
+
perTurn: "native",
|
|
18755
19147
|
afterCompact: "native",
|
|
18756
|
-
task: "single-active"
|
|
19148
|
+
task: "single-active",
|
|
19149
|
+
turnResend: "on-change"
|
|
18757
19150
|
},
|
|
18758
19151
|
subagents: {
|
|
18759
19152
|
identity: "native",
|
|
18760
19153
|
parentBinding: "none",
|
|
18761
19154
|
blockStart: "none",
|
|
18762
19155
|
worktreeIsolation: "undocumented",
|
|
18763
|
-
maxConcurrency: "undocumented"
|
|
19156
|
+
maxConcurrency: "undocumented",
|
|
19157
|
+
agentPrefix: "workit-"
|
|
18764
19158
|
},
|
|
18765
19159
|
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "native" },
|
|
18766
19160
|
interaction: { questions: "none", writeBoundary: "partial" },
|
|
@@ -18827,6 +19221,7 @@ var CODEX_DESCRIPTOR = {
|
|
|
18827
19221
|
};
|
|
18828
19222
|
var EVENTS2 = {
|
|
18829
19223
|
SessionStart: "session.start",
|
|
19224
|
+
UserPromptSubmit: "context.turn",
|
|
18830
19225
|
PreToolUse: "shell.pre",
|
|
18831
19226
|
PostToolUse: "shell.post",
|
|
18832
19227
|
SubagentStart: "subagent.start",
|
|
@@ -18834,7 +19229,7 @@ var EVENTS2 = {
|
|
|
18834
19229
|
};
|
|
18835
19230
|
var SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
|
|
18836
19231
|
var isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase());
|
|
18837
|
-
var SOURCES2 = new Set(["startup", "resume", "clear", "compact"]);
|
|
19232
|
+
var SOURCES2 = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
18838
19233
|
var parseCodexHookInput = (value) => {
|
|
18839
19234
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
|
|
18840
19235
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -18861,6 +19256,7 @@ var parseCodexHookInput = (value) => {
|
|
|
18861
19256
|
cwd,
|
|
18862
19257
|
...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
|
|
18863
19258
|
...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
|
|
19259
|
+
...event === "UserPromptSubmit" ? { prompt: optionalText(value.prompt) } : {},
|
|
18864
19260
|
...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
|
|
18865
19261
|
...toolEvent ? { tool_input: value.tool_input } : {},
|
|
18866
19262
|
...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
|
|
@@ -18880,13 +19276,20 @@ var protocolEvent = (input) => {
|
|
|
18880
19276
|
switch (input.hook_event_name) {
|
|
18881
19277
|
case "SessionStart":
|
|
18882
19278
|
return { kind: "session.start", source: input.source };
|
|
19279
|
+
case "UserPromptSubmit":
|
|
19280
|
+
return { kind: "context.turn", prompt: input.prompt ?? null };
|
|
18883
19281
|
case "PreToolUse": {
|
|
18884
19282
|
const toolUseId = input.tool_use_id ?? null;
|
|
18885
|
-
|
|
18886
|
-
kind: "shell.pre",
|
|
18887
|
-
|
|
18888
|
-
|
|
18889
|
-
|
|
19283
|
+
if (isShellTool(input.tool_name))
|
|
19284
|
+
return { kind: "shell.pre", command: input.command ?? "", toolUseId };
|
|
19285
|
+
if (isWriteTool(input.tool_name))
|
|
19286
|
+
return {
|
|
19287
|
+
kind: "write.pre",
|
|
19288
|
+
tool: input.tool_name,
|
|
19289
|
+
paths: writeTargets(input.tool_input),
|
|
19290
|
+
toolUseId
|
|
19291
|
+
};
|
|
19292
|
+
return { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
18890
19293
|
}
|
|
18891
19294
|
case "PostToolUse": {
|
|
18892
19295
|
const toolUseId = input.tool_use_id ?? null;
|
|
@@ -18962,12 +19365,12 @@ var codexAdapter = {
|
|
|
18962
19365
|
};
|
|
18963
19366
|
},
|
|
18964
19367
|
render: render2,
|
|
18965
|
-
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>)
|
|
19368
|
+
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>`
|
|
18966
19369
|
};
|
|
18967
19370
|
|
|
18968
19371
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
18969
|
-
import
|
|
18970
|
-
var
|
|
19372
|
+
import path21 from "node:path";
|
|
19373
|
+
var undocumented = { support: "undocumented", native: null };
|
|
18971
19374
|
var none = { support: "none", native: null };
|
|
18972
19375
|
var CURSOR_DESCRIPTOR = {
|
|
18973
19376
|
host: "cursor",
|
|
@@ -18981,26 +19384,28 @@ var CURSOR_DESCRIPTOR = {
|
|
|
18981
19384
|
"shell.pre": { support: "native", native: "beforeShellExecution" },
|
|
18982
19385
|
"tool.pre": { support: "native", native: "preToolUse" },
|
|
18983
19386
|
"write.pre": { support: "partial", native: "preToolUse" },
|
|
18984
|
-
"shell.post":
|
|
19387
|
+
"shell.post": undocumented,
|
|
18985
19388
|
"subagent.start": { support: "native", native: "subagentStart" },
|
|
18986
19389
|
"subagent.stop": { support: "partial", native: "subagentStop" },
|
|
18987
|
-
"prompt.submit":
|
|
19390
|
+
"prompt.submit": undocumented,
|
|
18988
19391
|
"compact.pre": { support: "partial", native: "preCompact" },
|
|
18989
|
-
stop:
|
|
19392
|
+
stop: none
|
|
18990
19393
|
},
|
|
18991
19394
|
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: true },
|
|
18992
19395
|
context: {
|
|
18993
19396
|
sessionStart: "native",
|
|
18994
19397
|
perTurn: "none",
|
|
18995
19398
|
afterCompact: "partial",
|
|
18996
|
-
task: "single-active"
|
|
19399
|
+
task: "single-active",
|
|
19400
|
+
turnResend: "every-turn"
|
|
18997
19401
|
},
|
|
18998
19402
|
subagents: {
|
|
18999
19403
|
identity: "native",
|
|
19000
19404
|
parentBinding: "native",
|
|
19001
19405
|
blockStart: "native",
|
|
19002
19406
|
worktreeIsolation: "undocumented",
|
|
19003
|
-
maxConcurrency: "undocumented"
|
|
19407
|
+
maxConcurrency: "undocumented",
|
|
19408
|
+
agentPrefix: "workit-"
|
|
19004
19409
|
},
|
|
19005
19410
|
provenance: {
|
|
19006
19411
|
sessionId: "native",
|
|
@@ -19125,7 +19530,7 @@ var protocolEvent2 = (input) => {
|
|
|
19125
19530
|
return isWriteTool(input.tool_name) ? {
|
|
19126
19531
|
kind: "write.pre",
|
|
19127
19532
|
tool: input.tool_name ?? "",
|
|
19128
|
-
paths:
|
|
19533
|
+
paths: writeTargets(input.tool_input),
|
|
19129
19534
|
toolUseId: null
|
|
19130
19535
|
} : { kind: "tool.pre", tool: input.tool_name ?? "", toolUseId: null };
|
|
19131
19536
|
case "subagentStart":
|
|
@@ -19147,7 +19552,7 @@ var protocolEvent2 = (input) => {
|
|
|
19147
19552
|
return { kind: "compact.pre", trigger: "auto" };
|
|
19148
19553
|
}
|
|
19149
19554
|
};
|
|
19150
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
19555
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path21.sep) ? root : `${root}${path21.sep}`);
|
|
19151
19556
|
var hookCwd = (input) => {
|
|
19152
19557
|
const cwd = existingDirectory(input.cwd);
|
|
19153
19558
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|