@brainervirus/workit-cursor 8.4.1 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +463 -77
- package/dist/mcp-server.js +40 -6
- package/dist/workit-hook.js +475 -75
- package/package.json +3 -3
- package/skills/workit-retro/SKILL.md +2 -2
|
@@ -17240,6 +17240,8 @@ var support = (descriptor, axis) => {
|
|
|
17240
17240
|
});
|
|
17241
17241
|
|
|
17242
17242
|
// packages/workit-core/src/hooks/context.ts
|
|
17243
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
17244
|
+
import fs13 from "node:fs";
|
|
17243
17245
|
import path16 from "node:path";
|
|
17244
17246
|
function sessionCompactContext(store, session, context, selection = "session-bound") {
|
|
17245
17247
|
const listed = store.listTaskIndex();
|
|
@@ -17368,9 +17370,71 @@ ${options.addendum}` : ""}
|
|
|
17368
17370
|
} catch {
|
|
17369
17371
|
return null;
|
|
17370
17372
|
}
|
|
17373
|
+
}, TURN_TTL_MS, digestOf = (text) => createHash10("sha256").update(text).digest("hex"), turnMarker = (input) => {
|
|
17374
|
+
if (!input.session.id)
|
|
17375
|
+
return null;
|
|
17376
|
+
try {
|
|
17377
|
+
const location = resolveStore(input.cwd);
|
|
17378
|
+
if (location instanceof Error)
|
|
17379
|
+
return null;
|
|
17380
|
+
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
17381
|
+
return path16.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
17382
|
+
} catch {
|
|
17383
|
+
return null;
|
|
17384
|
+
}
|
|
17385
|
+
}, writeTurnDigest = (file, digest) => {
|
|
17386
|
+
try {
|
|
17387
|
+
fs13.mkdirSync(path16.dirname(file), { recursive: true });
|
|
17388
|
+
fs13.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
17389
|
+
`);
|
|
17390
|
+
} catch {}
|
|
17391
|
+
}, clearTurnDigest = (file) => {
|
|
17392
|
+
try {
|
|
17393
|
+
fs13.rmSync(file, { force: true });
|
|
17394
|
+
} catch {}
|
|
17395
|
+
}, pruneTurnDigests = (dir, now) => {
|
|
17396
|
+
try {
|
|
17397
|
+
for (const name of fs13.readdirSync(dir)) {
|
|
17398
|
+
if (!name.startsWith("turn-"))
|
|
17399
|
+
continue;
|
|
17400
|
+
const file = path16.join(dir, name);
|
|
17401
|
+
try {
|
|
17402
|
+
if (now - fs13.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
17403
|
+
fs13.rmSync(file, { force: true });
|
|
17404
|
+
} catch {}
|
|
17405
|
+
}
|
|
17406
|
+
} catch {}
|
|
17407
|
+
}, changedTurnContext = (input, text) => {
|
|
17408
|
+
const file = turnMarker(input);
|
|
17409
|
+
if (!text) {
|
|
17410
|
+
if (file)
|
|
17411
|
+
clearTurnDigest(file);
|
|
17412
|
+
return null;
|
|
17413
|
+
}
|
|
17414
|
+
if (!file)
|
|
17415
|
+
return text;
|
|
17416
|
+
const digest = digestOf(text);
|
|
17417
|
+
try {
|
|
17418
|
+
const previous = JSON.parse(fs13.readFileSync(file, "utf8"));
|
|
17419
|
+
if (previous.digest === digest)
|
|
17420
|
+
return null;
|
|
17421
|
+
} catch {}
|
|
17422
|
+
writeTurnDigest(file, digest);
|
|
17423
|
+
return text;
|
|
17424
|
+
}, seedTurnContext = (input, descriptor) => {
|
|
17425
|
+
const file = turnMarker(input);
|
|
17426
|
+
if (!file)
|
|
17427
|
+
return;
|
|
17428
|
+
pruneTurnDigests(path16.dirname(file), Date.now());
|
|
17429
|
+
const text = turnContextText(input, descriptor);
|
|
17430
|
+
if (text)
|
|
17431
|
+
writeTurnDigest(file, digestOf(text));
|
|
17432
|
+
else
|
|
17433
|
+
clearTurnDigest(file);
|
|
17371
17434
|
};
|
|
17372
17435
|
var init_context = __esm(() => {
|
|
17373
17436
|
init_methods();
|
|
17437
|
+
init_paths();
|
|
17374
17438
|
init_task_contract();
|
|
17375
17439
|
init_task_evaluation();
|
|
17376
17440
|
init_rev();
|
|
@@ -17378,6 +17442,7 @@ var init_context = __esm(() => {
|
|
|
17378
17442
|
init_task_store();
|
|
17379
17443
|
signals = new Map;
|
|
17380
17444
|
cache = new Map;
|
|
17445
|
+
TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
17381
17446
|
});
|
|
17382
17447
|
|
|
17383
17448
|
// packages/workit-core/src/core/commit-flavors.ts
|
|
@@ -17387,7 +17452,7 @@ var init_commit_flavors = () => {};
|
|
|
17387
17452
|
var init_git = () => {};
|
|
17388
17453
|
|
|
17389
17454
|
// packages/workit-core/src/core/branch.ts
|
|
17390
|
-
import { createHash as
|
|
17455
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
17391
17456
|
function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
17392
17457
|
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
17393
17458
|
const config = readConfig();
|
|
@@ -17453,7 +17518,7 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
17453
17518
|
const source = resolved.provenance.branchPolicy;
|
|
17454
17519
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
17455
17520
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
17456
|
-
const fingerprint =
|
|
17521
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
17457
17522
|
return {
|
|
17458
17523
|
branchPolicy,
|
|
17459
17524
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -17642,7 +17707,7 @@ function writeGate(cwd, paths) {
|
|
|
17642
17707
|
const gate = gateFor(cwd);
|
|
17643
17708
|
if (!gate || gate.blockers.length === 0)
|
|
17644
17709
|
return NONE2;
|
|
17645
|
-
if (paths && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
17710
|
+
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
17646
17711
|
return NONE2;
|
|
17647
17712
|
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
17648
17713
|
return {
|
|
@@ -17812,8 +17877,8 @@ var init_write_gate = __esm(() => {
|
|
|
17812
17877
|
|
|
17813
17878
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
17814
17879
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17815
|
-
import { createHash as
|
|
17816
|
-
import
|
|
17880
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
17881
|
+
import fs14 from "node:fs";
|
|
17817
17882
|
import os4 from "node:os";
|
|
17818
17883
|
import path18 from "node:path";
|
|
17819
17884
|
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
@@ -17938,8 +18003,8 @@ function noteRawCommit(input, command, key) {
|
|
|
17938
18003
|
if (action.kind !== "commit")
|
|
17939
18004
|
continue;
|
|
17940
18005
|
const file = markerFile(repo, input.session.id, key);
|
|
17941
|
-
|
|
17942
|
-
|
|
18006
|
+
fs14.mkdirSync(path18.dirname(file), { recursive: true });
|
|
18007
|
+
fs14.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
17943
18008
|
`);
|
|
17944
18009
|
}
|
|
17945
18010
|
} catch {}
|
|
@@ -17961,7 +18026,7 @@ function settlePendingCommit(input) {
|
|
|
17961
18026
|
if (!repo)
|
|
17962
18027
|
return;
|
|
17963
18028
|
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
17964
|
-
if (
|
|
18029
|
+
if (fs14.existsSync(file))
|
|
17965
18030
|
settle2(input, repo, file);
|
|
17966
18031
|
} catch {}
|
|
17967
18032
|
}
|
|
@@ -18067,7 +18132,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18067
18132
|
return null;
|
|
18068
18133
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
18069
18134
|
}, isWorkitWorkspace = (repo) => {
|
|
18070
|
-
if (
|
|
18135
|
+
if (fs14.existsSync(repo.location.dir))
|
|
18071
18136
|
return true;
|
|
18072
18137
|
try {
|
|
18073
18138
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -18095,7 +18160,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18095
18160
|
if (!git || git.reftable)
|
|
18096
18161
|
return null;
|
|
18097
18162
|
try {
|
|
18098
|
-
const head =
|
|
18163
|
+
const head = fs14.readFileSync(path18.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18099
18164
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18100
18165
|
return head;
|
|
18101
18166
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
@@ -18103,9 +18168,9 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18103
18168
|
return null;
|
|
18104
18169
|
const common = path18.dirname(repo.location.dir);
|
|
18105
18170
|
try {
|
|
18106
|
-
return
|
|
18171
|
+
return fs14.readFileSync(path18.join(common, ref), "utf8").trim() || null;
|
|
18107
18172
|
} catch {}
|
|
18108
|
-
const packed =
|
|
18173
|
+
const packed = fs14.readFileSync(path18.join(common, "packed-refs"), "utf8");
|
|
18109
18174
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18110
18175
|
} catch {
|
|
18111
18176
|
return null;
|
|
@@ -18148,7 +18213,10 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18148
18213
|
out.push({ action, repo });
|
|
18149
18214
|
}
|
|
18150
18215
|
return out;
|
|
18151
|
-
},
|
|
18216
|
+
}, inWorkitWorkspace = (dir) => {
|
|
18217
|
+
const repo = repoAt(dir);
|
|
18218
|
+
return repo !== null && isWorkitWorkspace(repo);
|
|
18219
|
+
}, rawDelivery = (cwd, command, dialect) => mentionsRawTool(command) && actionsIn(cwd, command, dialect).some(({ action }) => action.kind !== "commit" && action.kind !== "pr-read"), mentionsRawTool = (command) => /\b(?:git|gh|glab)\b/i.test(command), dialectOf = (input) => (input.event.kind === "shell.pre" || input.event.kind === "shell.post" ? input.event.dialect : undefined) ?? "posix", deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path18.join(repo.location.dir, "hooks", `commit-${createHash12("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash12("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
|
|
18152
18220
|
const run = spawnSync11("git", [
|
|
18153
18221
|
"log",
|
|
18154
18222
|
"-1",
|
|
@@ -18165,11 +18233,11 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18165
18233
|
}, settle2 = (input, repo, file) => {
|
|
18166
18234
|
let marker;
|
|
18167
18235
|
try {
|
|
18168
|
-
marker = JSON.parse(
|
|
18236
|
+
marker = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
18169
18237
|
} catch {
|
|
18170
18238
|
return;
|
|
18171
18239
|
}
|
|
18172
|
-
|
|
18240
|
+
fs14.rmSync(file, { force: true });
|
|
18173
18241
|
const session = input.session.id;
|
|
18174
18242
|
const now = Date.now() / 1000;
|
|
18175
18243
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -18253,18 +18321,286 @@ var init_raw_git = __esm(() => {
|
|
|
18253
18321
|
};
|
|
18254
18322
|
});
|
|
18255
18323
|
|
|
18324
|
+
// packages/workit-core/src/core/skill-manifests.ts
|
|
18325
|
+
var WORKIT_METHOD_SKILLS, WORKIT_SKILL_ALIASES, skillLoadWording = (host, skill) => {
|
|
18326
|
+
switch (host) {
|
|
18327
|
+
case "claude-code":
|
|
18328
|
+
return `call the Skill tool with \`workit:${skill.replace(/^workit-/, "")}\``;
|
|
18329
|
+
case "opencode":
|
|
18330
|
+
return `call the skill tool with \`${skill}\``;
|
|
18331
|
+
default:
|
|
18332
|
+
return `read the \`${skill}\` skill's SKILL.md and follow it`;
|
|
18333
|
+
}
|
|
18334
|
+
};
|
|
18335
|
+
var init_skill_manifests = __esm(() => {
|
|
18336
|
+
WORKIT_METHOD_SKILLS = [
|
|
18337
|
+
"workit-shape",
|
|
18338
|
+
"workit-implement",
|
|
18339
|
+
"workit-review",
|
|
18340
|
+
"workit-debug",
|
|
18341
|
+
"workit-ship",
|
|
18342
|
+
"workit-continue",
|
|
18343
|
+
"workit-bdd",
|
|
18344
|
+
"workit-test-audit",
|
|
18345
|
+
"workit-deslop",
|
|
18346
|
+
"workit-fanout",
|
|
18347
|
+
"workit-verify-app",
|
|
18348
|
+
"workit-retro",
|
|
18349
|
+
"workit-architecture"
|
|
18350
|
+
];
|
|
18351
|
+
WORKIT_SKILL_ALIASES = {
|
|
18352
|
+
"wk-shape": "workit-shape",
|
|
18353
|
+
"wk-implement": "workit-implement",
|
|
18354
|
+
"wk-review": "workit-review",
|
|
18355
|
+
"wk-debug": "workit-debug",
|
|
18356
|
+
"wk-ship": "workit-ship",
|
|
18357
|
+
"wk-continue": "workit-continue",
|
|
18358
|
+
"wk-bdd": "workit-bdd",
|
|
18359
|
+
"wk-test-audit": "workit-test-audit",
|
|
18360
|
+
"wk-deslop": "workit-deslop",
|
|
18361
|
+
"wk-fanout": "workit-fanout",
|
|
18362
|
+
"wk-verify-app": "workit-verify-app",
|
|
18363
|
+
"wk-retro": "workit-retro",
|
|
18364
|
+
"wk-architecture": "workit-architecture"
|
|
18365
|
+
};
|
|
18366
|
+
if (false) {}
|
|
18367
|
+
});
|
|
18368
|
+
|
|
18369
|
+
// packages/workit-core/src/hooks/skill-nudge.ts
|
|
18370
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
18371
|
+
import fs15 from "node:fs";
|
|
18372
|
+
import path19 from "node:path";
|
|
18373
|
+
function recordSkillLoad(input, name, via) {
|
|
18374
|
+
try {
|
|
18375
|
+
const skill = workitSkillOf(name);
|
|
18376
|
+
const session = input.session.id;
|
|
18377
|
+
if (!skill || !session)
|
|
18378
|
+
return;
|
|
18379
|
+
if (!inWorkitWorkspace(input.cwd))
|
|
18380
|
+
return;
|
|
18381
|
+
const file = markerFile2(input, session);
|
|
18382
|
+
if (!file)
|
|
18383
|
+
return;
|
|
18384
|
+
const marker = readMarker(file);
|
|
18385
|
+
if (marker.loaded.includes(skill))
|
|
18386
|
+
return;
|
|
18387
|
+
writeMarker(file, { ...marker, loaded: [...marker.loaded, skill] });
|
|
18388
|
+
if (loadedSkills(input.cwd, session).has(skill))
|
|
18389
|
+
return;
|
|
18390
|
+
appendHookObserved(input.cwd, {
|
|
18391
|
+
type: "skill.loaded",
|
|
18392
|
+
actor: { host: input.host, session, agentId: input.session.agentId },
|
|
18393
|
+
skill,
|
|
18394
|
+
via
|
|
18395
|
+
});
|
|
18396
|
+
} catch {}
|
|
18397
|
+
}
|
|
18398
|
+
function promptNudge(input, prompt) {
|
|
18399
|
+
try {
|
|
18400
|
+
const session = input.session.id;
|
|
18401
|
+
if (!prompt || !session || input.session.agentId)
|
|
18402
|
+
return null;
|
|
18403
|
+
const loads = promptLoads(prompt);
|
|
18404
|
+
if (loads) {
|
|
18405
|
+
recordSkillLoad(input, loads, "slash");
|
|
18406
|
+
return null;
|
|
18407
|
+
}
|
|
18408
|
+
const hit = promptTrigger(prompt);
|
|
18409
|
+
if (!hit || !inWorkitWorkspace(input.cwd))
|
|
18410
|
+
return null;
|
|
18411
|
+
return nudgeOnce(input, session, hit.skill, nudgeText(input, hit.skill, `this request ("${hit.trigger}")`));
|
|
18412
|
+
} catch {
|
|
18413
|
+
return null;
|
|
18414
|
+
}
|
|
18415
|
+
}
|
|
18416
|
+
function shipNudge(input, command) {
|
|
18417
|
+
try {
|
|
18418
|
+
const session = input.session.id;
|
|
18419
|
+
if (!session || input.session.agentId)
|
|
18420
|
+
return null;
|
|
18421
|
+
const dialect = input.event.kind === "shell.pre" ? input.event.dialect ?? "posix" : "posix";
|
|
18422
|
+
if (!isDeliveryCommand(input.cwd, command, dialect))
|
|
18423
|
+
return null;
|
|
18424
|
+
return nudgeOnce(input, session, "workit-ship", nudgeText(input, "workit-ship", "this delivery step"));
|
|
18425
|
+
} catch {
|
|
18426
|
+
return null;
|
|
18427
|
+
}
|
|
18428
|
+
}
|
|
18429
|
+
var USER_INVOKED, SKILL_HOST, PROMPT_INTENTS, workitSkillOf = (name) => {
|
|
18430
|
+
const bare = name.trim().replace(/^\//, "").toLowerCase();
|
|
18431
|
+
const alias = WORKIT_SKILL_ALIASES[bare];
|
|
18432
|
+
if (alias)
|
|
18433
|
+
return alias;
|
|
18434
|
+
const skill = bare.replace(/^workit:/, "workit-");
|
|
18435
|
+
return WORKIT_METHOD_SKILLS.includes(skill) ? skill : null;
|
|
18436
|
+
}, promptTrigger = (prompt) => {
|
|
18437
|
+
const text = prompt.replace(/```[\s\S]*?```/g, " ").slice(0, 4000);
|
|
18438
|
+
for (const [skill, pattern] of PROMPT_INTENTS) {
|
|
18439
|
+
const match = pattern.exec(text);
|
|
18440
|
+
if (match)
|
|
18441
|
+
return { skill, trigger: match[1] ?? match[0] };
|
|
18442
|
+
}
|
|
18443
|
+
return null;
|
|
18444
|
+
}, markerFile2 = (input, session) => {
|
|
18445
|
+
const location = resolveStore(input.cwd);
|
|
18446
|
+
if (location instanceof Error || !location.shared)
|
|
18447
|
+
return null;
|
|
18448
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18449
|
+
return path19.join(location.dir, "hooks", `skills-${name}.json`);
|
|
18450
|
+
}, readMarker = (file) => {
|
|
18451
|
+
try {
|
|
18452
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
18453
|
+
return { nudged: value.nudged ?? [], loaded: value.loaded ?? [] };
|
|
18454
|
+
} catch {
|
|
18455
|
+
return { nudged: [], loaded: [] };
|
|
18456
|
+
}
|
|
18457
|
+
}, writeMarker = (file, marker) => {
|
|
18458
|
+
try {
|
|
18459
|
+
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18460
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
18461
|
+
} catch {}
|
|
18462
|
+
}, loadedSkills = (cwd, session) => {
|
|
18463
|
+
const ledger = readLedger(cwd);
|
|
18464
|
+
const loaded = new Set;
|
|
18465
|
+
if (!ledger.ok)
|
|
18466
|
+
return loaded;
|
|
18467
|
+
for (const row of ledger.value.rows)
|
|
18468
|
+
if (row.type === "skill.loaded" && row.actor.session === session && typeof row.skill === "string")
|
|
18469
|
+
loaded.add(row.skill);
|
|
18470
|
+
return loaded;
|
|
18471
|
+
}, SKILL_FILE, skillFileIn = (file) => SKILL_FILE.exec(file)?.[1] ?? null, READERS, skillReadIn = (command, dialect = "posix") => {
|
|
18472
|
+
for (const { words } of segmentsOf(command, dialect)) {
|
|
18473
|
+
if (!READERS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
|
|
18474
|
+
continue;
|
|
18475
|
+
for (const word of words.slice(1)) {
|
|
18476
|
+
const skill = skillFileIn(word);
|
|
18477
|
+
if (skill)
|
|
18478
|
+
return skill;
|
|
18479
|
+
}
|
|
18480
|
+
}
|
|
18481
|
+
return null;
|
|
18482
|
+
}, nudgeOnce = (input, session, skill, line) => {
|
|
18483
|
+
const file = markerFile2(input, session);
|
|
18484
|
+
if (!file)
|
|
18485
|
+
return null;
|
|
18486
|
+
const marker = readMarker(file);
|
|
18487
|
+
if (marker.nudged.includes(skill) || marker.loaded.includes(skill))
|
|
18488
|
+
return null;
|
|
18489
|
+
if (loadedSkills(input.cwd, session).has(skill))
|
|
18490
|
+
return null;
|
|
18491
|
+
writeMarker(file, { ...marker, nudged: [...marker.nudged, skill] });
|
|
18492
|
+
return line;
|
|
18493
|
+
}, 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.`, 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, WORKIT_DELIVERY, SHELLS2, LAUNCHERS, runsWorkitDelivery = (command, dialect, depth = 0) => segmentsOf(command, dialect).some(({ words }) => {
|
|
18494
|
+
const at = words.findIndex((word) => !/^\w+=/.test(word) && !LAUNCHERS.has(word));
|
|
18495
|
+
if (at >= 0 && /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/.test(words[at])) {
|
|
18496
|
+
const args = words.slice(at + 1);
|
|
18497
|
+
if (args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run"))
|
|
18498
|
+
return false;
|
|
18499
|
+
return WORKIT_DELIVERY.test(args.join(" "));
|
|
18500
|
+
}
|
|
18501
|
+
const shell = SHELLS2.has(words[at]?.split(/[\\/]/).at(-1) ?? "");
|
|
18502
|
+
const script = shell && words[at + 1] === "-c" ? words[at + 2] : undefined;
|
|
18503
|
+
return depth < 3 && script !== undefined && runsWorkitDelivery(script, "posix", depth + 1);
|
|
18504
|
+
}), isDeliveryCommand = (cwd, command, dialect = "posix") => rawDelivery(cwd, command, dialect) || runsWorkitDelivery(command, dialect) && inWorkitWorkspace(cwd), withContextLine = (decision, line) => {
|
|
18505
|
+
if (!line)
|
|
18506
|
+
return decision;
|
|
18507
|
+
if (decision.kind === "none")
|
|
18508
|
+
return { kind: "context", text: line };
|
|
18509
|
+
if (decision.kind === "context")
|
|
18510
|
+
return { kind: "context", text: `${decision.text}
|
|
18511
|
+
${line}` };
|
|
18512
|
+
return decision;
|
|
18513
|
+
};
|
|
18514
|
+
var init_skill_nudge = __esm(() => {
|
|
18515
|
+
init_skill_manifests();
|
|
18516
|
+
init_ledger();
|
|
18517
|
+
init_paths();
|
|
18518
|
+
init_raw_git();
|
|
18519
|
+
USER_INVOKED = new Set(["workit-retro", "workit-architecture"]);
|
|
18520
|
+
SKILL_HOST = {
|
|
18521
|
+
claude_code: "claude-code",
|
|
18522
|
+
opencode: "opencode",
|
|
18523
|
+
codex_cli: "codex",
|
|
18524
|
+
codex_desktop: "codex",
|
|
18525
|
+
cursor: "cursor",
|
|
18526
|
+
pi: "pi"
|
|
18527
|
+
};
|
|
18528
|
+
PROMPT_INTENTS = [
|
|
18529
|
+
[
|
|
18530
|
+
"workit-debug",
|
|
18531
|
+
/\b(flaky|regression|(?:is|are|got|looks) broken|debug (?:this|it|the|why)|fix (?:the|this|a) bug)\b/i
|
|
18532
|
+
],
|
|
18533
|
+
[
|
|
18534
|
+
"workit-continue",
|
|
18535
|
+
/\b(pick up where|where (?:were|did) we|resume (?:the|this|my|our|work|where)|hand ?off)\b/i
|
|
18536
|
+
],
|
|
18537
|
+
[
|
|
18538
|
+
"workit-review",
|
|
18539
|
+
/\b(blast radius|code review|review (?:the|this|my|PR|pr|#\d+|branch|diff|changes))\b/i
|
|
18540
|
+
],
|
|
18541
|
+
[
|
|
18542
|
+
"workit-ship",
|
|
18543
|
+
/\b(ship (?:it|this|the)|babysit|open (?:a|the) (?:PR|pr|MR|mr)|merge (?:the|this|my) (?:PR|pr|MR|mr|branch)|(?:get|until|make) CI (?:is )?green)\b/i
|
|
18544
|
+
],
|
|
18545
|
+
["workit-bdd", /\b(BDD|TDD|acceptance criteria|Given\/When\/Then)(?![\w/])/],
|
|
18546
|
+
["workit-test-audit", /\b(test audit|audit the tests|tautolog\w*|weak tests)\b/i],
|
|
18547
|
+
["workit-deslop", /\b(deslop|slop|dead code)\b/i],
|
|
18548
|
+
["workit-fanout", /\b(fan(?: |-)?out|parallelize|parallel agents|swarm)\b/i],
|
|
18549
|
+
["workit-verify-app", /\b(verify the app|smoke test|prove it works)\b/i],
|
|
18550
|
+
["workit-retro", /\b(retro|retrospective)\b(?!\.\w)/i],
|
|
18551
|
+
["workit-architecture", /\b(deepen (?:the )?modules|architecture (?:review|pass|audit))\b/i],
|
|
18552
|
+
[
|
|
18553
|
+
"workit-shape",
|
|
18554
|
+
/\b(brainstorm\w*|grill me|should we|let'?s plan|plan (?:out|how|the|a|for)|(?:write|draft) (?:a|the) spec)\b/i
|
|
18555
|
+
],
|
|
18556
|
+
[
|
|
18557
|
+
"workit-implement",
|
|
18558
|
+
/\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
|
|
18559
|
+
]
|
|
18560
|
+
];
|
|
18561
|
+
SKILL_FILE = /(?:^|[\\/])(workit-[a-z-]+)[\\/]SKILL\.md$/;
|
|
18562
|
+
READERS = new Set([
|
|
18563
|
+
"cat",
|
|
18564
|
+
"sed",
|
|
18565
|
+
"head",
|
|
18566
|
+
"tail",
|
|
18567
|
+
"less",
|
|
18568
|
+
"more",
|
|
18569
|
+
"bat",
|
|
18570
|
+
"nl",
|
|
18571
|
+
"get-content",
|
|
18572
|
+
"gc",
|
|
18573
|
+
"type"
|
|
18574
|
+
]);
|
|
18575
|
+
WORKIT_DELIVERY = /^(?:pr (?:create|merge)|ci wait|git push)\b/;
|
|
18576
|
+
SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
18577
|
+
LAUNCHERS = new Set([
|
|
18578
|
+
"npx",
|
|
18579
|
+
"bunx",
|
|
18580
|
+
"pnpx",
|
|
18581
|
+
"-y",
|
|
18582
|
+
"--yes",
|
|
18583
|
+
"pnpm",
|
|
18584
|
+
"dlx",
|
|
18585
|
+
"exec",
|
|
18586
|
+
"env",
|
|
18587
|
+
"sudo",
|
|
18588
|
+
"command"
|
|
18589
|
+
]);
|
|
18590
|
+
});
|
|
18591
|
+
|
|
18256
18592
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18257
18593
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
18258
|
-
import
|
|
18594
|
+
import path20 from "node:path";
|
|
18259
18595
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
|
|
18260
|
-
if (!nonEmpty2(value) || !
|
|
18596
|
+
if (!nonEmpty2(value) || !path20.isAbsolute(value) || !existsSync6(value))
|
|
18261
18597
|
return null;
|
|
18262
18598
|
try {
|
|
18263
18599
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
18264
18600
|
} catch {
|
|
18265
18601
|
return null;
|
|
18266
18602
|
}
|
|
18267
|
-
},
|
|
18603
|
+
}, SHELLS3, SCRIPT_FLAG2, quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg), commandText = (value) => {
|
|
18268
18604
|
if (nonEmpty2(value))
|
|
18269
18605
|
return value;
|
|
18270
18606
|
if (!Array.isArray(value) || value.length === 0)
|
|
@@ -18273,7 +18609,7 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
18273
18609
|
if (!argv.every((arg) => typeof arg === "string"))
|
|
18274
18610
|
return null;
|
|
18275
18611
|
const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
|
|
18276
|
-
if (
|
|
18612
|
+
if (SHELLS3.has(shell))
|
|
18277
18613
|
for (let index = 1;index < argv.length - 1; index++) {
|
|
18278
18614
|
if (SCRIPT_FLAG2.test(argv[index]))
|
|
18279
18615
|
return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
|
|
@@ -18282,18 +18618,21 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
18282
18618
|
}
|
|
18283
18619
|
const joined = argv.map(quoteArg).join(" ");
|
|
18284
18620
|
return nonEmpty2(joined) ? joined : null;
|
|
18285
|
-
}, WRITE_TOOLS, isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, "")), PATH_KEYS, writePaths = (input) => {
|
|
18621
|
+
}, WRITE_TOOLS, isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, "")), PATH_KEYS, PATCH_HEADER, writePaths = (input) => {
|
|
18286
18622
|
if (!isRecord4(input))
|
|
18287
18623
|
return [];
|
|
18288
18624
|
const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
|
|
18289
18625
|
for (const value of Object.values(input))
|
|
18290
18626
|
if (typeof value === "string")
|
|
18291
|
-
for (const match of value.matchAll(
|
|
18627
|
+
for (const match of value.matchAll(PATCH_HEADER))
|
|
18292
18628
|
paths.push(match[1].trim());
|
|
18293
18629
|
return [...new Set(paths)];
|
|
18630
|
+
}, writeTargets = (input) => {
|
|
18631
|
+
const paths = writePaths(input);
|
|
18632
|
+
return paths.length > 0 ? paths : null;
|
|
18294
18633
|
};
|
|
18295
18634
|
var init_fields = __esm(() => {
|
|
18296
|
-
|
|
18635
|
+
SHELLS3 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
18297
18636
|
SCRIPT_FLAG2 = /^(?:-\w*c|-command|\/c)$/i;
|
|
18298
18637
|
WRITE_TOOLS = new Set([
|
|
18299
18638
|
"write",
|
|
@@ -18315,6 +18654,7 @@ var init_fields = __esm(() => {
|
|
|
18315
18654
|
"writefile"
|
|
18316
18655
|
]);
|
|
18317
18656
|
PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
|
|
18657
|
+
PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
|
|
18318
18658
|
});
|
|
18319
18659
|
|
|
18320
18660
|
// packages/workit-core/src/hooks/handle.ts
|
|
@@ -18332,18 +18672,22 @@ function handleHook(input, deps) {
|
|
|
18332
18672
|
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.`;
|
|
18333
18673
|
const addendum = [deps.addendum, advisory].filter(Boolean).join(`
|
|
18334
18674
|
`) || null;
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
|
|
18338
|
-
};
|
|
18675
|
+
const text = sessionContextText(input, descriptor, { offer, addendum });
|
|
18676
|
+
if (resendsOnChange(descriptor))
|
|
18677
|
+
seedTurnContext(input, descriptor);
|
|
18678
|
+
return { kind: "context", text };
|
|
18339
18679
|
}
|
|
18340
18680
|
case "context.turn": {
|
|
18341
18681
|
if (!usable2(descriptor.context.perTurn))
|
|
18342
18682
|
return NONE4;
|
|
18343
|
-
const
|
|
18344
|
-
|
|
18683
|
+
const current = turnContextText(input, descriptor);
|
|
18684
|
+
const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
|
|
18685
|
+
return withContextLine(text ? { kind: "context", text } : NONE4, promptNudge(input, event.prompt));
|
|
18345
18686
|
}
|
|
18346
18687
|
case "shell.pre": {
|
|
18688
|
+
const skillRead = skillReadIn(event.command, event.dialect);
|
|
18689
|
+
if (skillRead)
|
|
18690
|
+
recordSkillLoad(input, skillRead, "read");
|
|
18347
18691
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
18348
18692
|
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
|
|
18349
18693
|
if (policy.kind !== "none")
|
|
@@ -18359,7 +18703,7 @@ function handleHook(input, deps) {
|
|
|
18359
18703
|
if (gate.kind === "deny")
|
|
18360
18704
|
return gate;
|
|
18361
18705
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
18362
|
-
return raw;
|
|
18706
|
+
return withContextLine(raw, shipNudge(input, event.command));
|
|
18363
18707
|
}
|
|
18364
18708
|
case "shell.post":
|
|
18365
18709
|
if (usable2(descriptor.events["shell.post"].support))
|
|
@@ -18370,7 +18714,7 @@ function handleHook(input, deps) {
|
|
|
18370
18714
|
case "subagent.start":
|
|
18371
18715
|
return {
|
|
18372
18716
|
kind: "context",
|
|
18373
|
-
text: subagentStartText(input.
|
|
18717
|
+
text: subagentStartText(input.session.id ?? null, descriptor, event)
|
|
18374
18718
|
};
|
|
18375
18719
|
case "compact.pre":
|
|
18376
18720
|
return {
|
|
@@ -18378,19 +18722,31 @@ function handleHook(input, deps) {
|
|
|
18378
18722
|
userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
|
|
18379
18723
|
};
|
|
18380
18724
|
case "tool.pre":
|
|
18725
|
+
if (event.skill)
|
|
18726
|
+
recordSkillLoad(input, event.skill, "tool");
|
|
18727
|
+
return NONE4;
|
|
18381
18728
|
case "subagent.stop":
|
|
18382
18729
|
case "prompt.submit":
|
|
18383
18730
|
case "stop":
|
|
18384
18731
|
return NONE4;
|
|
18385
18732
|
}
|
|
18386
18733
|
}
|
|
18387
|
-
var usable2 = (value) => value === "native" || value === "partial", NONE4,
|
|
18388
|
-
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18734
|
+
var usable2 = (value) => value === "native" || value === "partial", NONE4, resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn), offered, ROLES, workitRole = (descriptor, agentType) => {
|
|
18735
|
+
const prefix = descriptor.subagents.agentPrefix;
|
|
18736
|
+
const name = agentType.startsWith(prefix) ? agentType.slice(prefix.length) : "";
|
|
18737
|
+
return Object.hasOwn(ROLES, name) ? ROLES[name] : null;
|
|
18738
|
+
}, subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128), subagentStartText = (session, descriptor, event) => {
|
|
18739
|
+
const role = workitRole(descriptor, event.agentType);
|
|
18740
|
+
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
18741
|
+
if (role === "implementer") {
|
|
18742
|
+
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";
|
|
18743
|
+
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.`;
|
|
18744
|
+
}
|
|
18745
|
+
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
18746
|
+
if (role !== "judge" || !event.agentId)
|
|
18747
|
+
return readOnly;
|
|
18748
|
+
const own = subagentSession(session, event.agentId);
|
|
18749
|
+
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.`;
|
|
18394
18750
|
}, failureDecision = (descriptor, event, error) => {
|
|
18395
18751
|
if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
|
|
18396
18752
|
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE4;
|
|
@@ -18423,10 +18779,15 @@ var init_handle = __esm(() => {
|
|
|
18423
18779
|
init_context();
|
|
18424
18780
|
init_policy();
|
|
18425
18781
|
init_raw_git();
|
|
18782
|
+
init_skill_nudge();
|
|
18426
18783
|
init_write_gate();
|
|
18427
18784
|
NONE4 = { kind: "none" };
|
|
18428
18785
|
offered = new Set;
|
|
18429
|
-
|
|
18786
|
+
ROLES = {
|
|
18787
|
+
implementer: "implementer",
|
|
18788
|
+
verifier: "judge",
|
|
18789
|
+
reviewer: "judge"
|
|
18790
|
+
};
|
|
18430
18791
|
});
|
|
18431
18792
|
|
|
18432
18793
|
// packages/workit-core/src/hooks/hosts/claude-code.ts
|
|
@@ -18436,7 +18797,7 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
18436
18797
|
case "SessionStart":
|
|
18437
18798
|
return SOURCES.has(value.source) ? { ok: true, event: { kind: "session.start", source: value.source } } : { ok: false, error: "SessionStart source is required" };
|
|
18438
18799
|
case "UserPromptSubmit":
|
|
18439
|
-
return { ok: true, event: { kind: "context.turn" } };
|
|
18800
|
+
return { ok: true, event: { kind: "context.turn", prompt: optionalText(value.prompt) } };
|
|
18440
18801
|
case "PreToolUse": {
|
|
18441
18802
|
if (!nonEmpty2(value.tool_name))
|
|
18442
18803
|
return { ok: false, error: "tool_name is required" };
|
|
@@ -18446,10 +18807,20 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
18446
18807
|
event: {
|
|
18447
18808
|
kind: "write.pre",
|
|
18448
18809
|
tool: value.tool_name,
|
|
18449
|
-
paths:
|
|
18810
|
+
paths: writeTargets(value.tool_input),
|
|
18450
18811
|
toolUseId
|
|
18451
18812
|
}
|
|
18452
18813
|
};
|
|
18814
|
+
if (value.tool_name === "Skill")
|
|
18815
|
+
return {
|
|
18816
|
+
ok: true,
|
|
18817
|
+
event: {
|
|
18818
|
+
kind: "tool.pre",
|
|
18819
|
+
tool: value.tool_name,
|
|
18820
|
+
toolUseId,
|
|
18821
|
+
skill: isRecord4(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
18822
|
+
}
|
|
18823
|
+
};
|
|
18453
18824
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
18454
18825
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
18455
18826
|
const command = toolCommand(value);
|
|
@@ -18547,24 +18918,26 @@ var init_claude_code = __esm(() => {
|
|
|
18547
18918
|
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18548
18919
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18549
18920
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18550
|
-
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18551
18921
|
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18552
|
-
"
|
|
18553
|
-
|
|
18922
|
+
"subagent.stop": { support: "none", native: null },
|
|
18923
|
+
"compact.pre": { support: "none", native: null },
|
|
18924
|
+
stop: { support: "none", native: null }
|
|
18554
18925
|
},
|
|
18555
18926
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18556
18927
|
context: {
|
|
18557
18928
|
sessionStart: "native",
|
|
18558
18929
|
perTurn: "native",
|
|
18559
18930
|
afterCompact: "native",
|
|
18560
|
-
task: "session-bound"
|
|
18931
|
+
task: "session-bound",
|
|
18932
|
+
turnResend: "every-turn"
|
|
18561
18933
|
},
|
|
18562
18934
|
subagents: {
|
|
18563
18935
|
identity: "native",
|
|
18564
18936
|
parentBinding: "partial",
|
|
18565
18937
|
blockStart: "none",
|
|
18566
18938
|
worktreeIsolation: "native",
|
|
18567
|
-
maxConcurrency: "undocumented"
|
|
18939
|
+
maxConcurrency: "undocumented",
|
|
18940
|
+
agentPrefix: "workit:"
|
|
18568
18941
|
},
|
|
18569
18942
|
provenance: { sessionId: "native", agentIdOnTool: "native", postToolObserve: "native" },
|
|
18570
18943
|
interaction: { questions: "undocumented", writeBoundary: "partial" },
|
|
@@ -18582,13 +18955,13 @@ var init_claude_code = __esm(() => {
|
|
|
18582
18955
|
},
|
|
18583
18956
|
{
|
|
18584
18957
|
name: "native_subagents",
|
|
18585
|
-
surface: "SubagentStart
|
|
18586
|
-
refs: ["SubagentStart"
|
|
18587
|
-
requires: ["event:subagent.start"
|
|
18588
|
-
observed: ["subagent.start"
|
|
18958
|
+
surface: "SubagentStart",
|
|
18959
|
+
refs: ["SubagentStart"],
|
|
18960
|
+
requires: ["event:subagent.start"],
|
|
18961
|
+
observed: ["subagent.start"],
|
|
18589
18962
|
assurance: "agent_guided",
|
|
18590
18963
|
reason: "Claude Code reports stable agent identities, but SubagentStart cannot block or bind",
|
|
18591
|
-
unavailableReason: "Claude Code
|
|
18964
|
+
unavailableReason: "Claude Code SubagentStart is unavailable"
|
|
18592
18965
|
},
|
|
18593
18966
|
{
|
|
18594
18967
|
name: "fresh-context-review",
|
|
@@ -18684,7 +19057,7 @@ var init_claude_code = __esm(() => {
|
|
|
18684
19057
|
function detectCodexSurface(env) {
|
|
18685
19058
|
return env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || Boolean(env.CODEX_ELECTRON_RESOURCES_PATH) ? "codex_desktop" : "codex_cli";
|
|
18686
19059
|
}
|
|
18687
|
-
var
|
|
19060
|
+
var CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase()), SOURCES2, parseCodexHookInput = (value) => {
|
|
18688
19061
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
|
|
18689
19062
|
return { ok: false, error: "hook_event_name is required" };
|
|
18690
19063
|
const event = value.hook_event_name;
|
|
@@ -18710,6 +19083,7 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
18710
19083
|
cwd,
|
|
18711
19084
|
...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
|
|
18712
19085
|
...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
|
|
19086
|
+
...event === "UserPromptSubmit" ? { prompt: optionalText(value.prompt) } : {},
|
|
18713
19087
|
...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
|
|
18714
19088
|
...toolEvent ? { tool_input: value.tool_input } : {},
|
|
18715
19089
|
...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
|
|
@@ -18728,13 +19102,20 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
18728
19102
|
switch (input.hook_event_name) {
|
|
18729
19103
|
case "SessionStart":
|
|
18730
19104
|
return { kind: "session.start", source: input.source };
|
|
19105
|
+
case "UserPromptSubmit":
|
|
19106
|
+
return { kind: "context.turn", prompt: input.prompt ?? null };
|
|
18731
19107
|
case "PreToolUse": {
|
|
18732
19108
|
const toolUseId = input.tool_use_id ?? null;
|
|
18733
|
-
|
|
18734
|
-
kind: "shell.pre",
|
|
18735
|
-
|
|
18736
|
-
|
|
18737
|
-
|
|
19109
|
+
if (isShellTool(input.tool_name))
|
|
19110
|
+
return { kind: "shell.pre", command: input.command ?? "", toolUseId };
|
|
19111
|
+
if (isWriteTool(input.tool_name))
|
|
19112
|
+
return {
|
|
19113
|
+
kind: "write.pre",
|
|
19114
|
+
tool: input.tool_name,
|
|
19115
|
+
paths: writeTargets(input.tool_input),
|
|
19116
|
+
toolUseId
|
|
19117
|
+
};
|
|
19118
|
+
return { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
18738
19119
|
}
|
|
18739
19120
|
case "PostToolUse": {
|
|
18740
19121
|
const toolUseId = input.tool_use_id ?? null;
|
|
@@ -18781,39 +19162,40 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
18781
19162
|
}, codexAdapter;
|
|
18782
19163
|
var init_codex = __esm(() => {
|
|
18783
19164
|
init_fields();
|
|
18784
|
-
undocumented = { support: "undocumented", native: null };
|
|
18785
19165
|
CODEX_DESCRIPTOR = {
|
|
18786
19166
|
host: "codex_cli",
|
|
18787
19167
|
label: "Codex",
|
|
18788
|
-
verifiedAgainst: "codex-cli 0.
|
|
19168
|
+
verifiedAgainst: "codex-cli 0.160.1 (hook JSON schemas in the binary); Codex Desktop 26.901.20858",
|
|
18789
19169
|
docs: ["https://developers.openai.com/codex/hooks"],
|
|
18790
19170
|
transport: "hook-process",
|
|
18791
19171
|
events: {
|
|
18792
19172
|
"session.start": { support: "native", native: "SessionStart" },
|
|
18793
|
-
"context.turn":
|
|
19173
|
+
"context.turn": { support: "native", native: "UserPromptSubmit" },
|
|
18794
19174
|
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
18795
19175
|
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
18796
|
-
"write.pre": { support: "
|
|
19176
|
+
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18797
19177
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18798
19178
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18799
19179
|
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18800
|
-
"prompt.submit":
|
|
19180
|
+
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18801
19181
|
"compact.pre": { support: "none", native: null },
|
|
18802
|
-
stop:
|
|
19182
|
+
stop: { support: "none", native: null }
|
|
18803
19183
|
},
|
|
18804
19184
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18805
19185
|
context: {
|
|
18806
19186
|
sessionStart: "native",
|
|
18807
|
-
perTurn: "
|
|
19187
|
+
perTurn: "native",
|
|
18808
19188
|
afterCompact: "native",
|
|
18809
|
-
task: "single-active"
|
|
19189
|
+
task: "single-active",
|
|
19190
|
+
turnResend: "on-change"
|
|
18810
19191
|
},
|
|
18811
19192
|
subagents: {
|
|
18812
19193
|
identity: "native",
|
|
18813
19194
|
parentBinding: "none",
|
|
18814
19195
|
blockStart: "none",
|
|
18815
19196
|
worktreeIsolation: "undocumented",
|
|
18816
|
-
maxConcurrency: "undocumented"
|
|
19197
|
+
maxConcurrency: "undocumented",
|
|
19198
|
+
agentPrefix: "workit-"
|
|
18817
19199
|
},
|
|
18818
19200
|
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "native" },
|
|
18819
19201
|
interaction: { questions: "none", writeBoundary: "partial" },
|
|
@@ -18880,13 +19262,14 @@ var init_codex = __esm(() => {
|
|
|
18880
19262
|
};
|
|
18881
19263
|
EVENTS2 = {
|
|
18882
19264
|
SessionStart: "session.start",
|
|
19265
|
+
UserPromptSubmit: "context.turn",
|
|
18883
19266
|
PreToolUse: "shell.pre",
|
|
18884
19267
|
PostToolUse: "shell.post",
|
|
18885
19268
|
SubagentStart: "subagent.start",
|
|
18886
19269
|
SubagentStop: "subagent.stop"
|
|
18887
19270
|
};
|
|
18888
19271
|
SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
|
|
18889
|
-
SOURCES2 = new Set(["startup", "resume", "clear", "compact"]);
|
|
19272
|
+
SOURCES2 = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
18890
19273
|
codexAdapter = {
|
|
18891
19274
|
descriptor: CODEX_DESCRIPTOR,
|
|
18892
19275
|
parse(raw, env) {
|
|
@@ -18916,13 +19299,13 @@ var init_codex = __esm(() => {
|
|
|
18916
19299
|
};
|
|
18917
19300
|
},
|
|
18918
19301
|
render: render2,
|
|
18919
|
-
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>)
|
|
19302
|
+
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>`
|
|
18920
19303
|
};
|
|
18921
19304
|
});
|
|
18922
19305
|
|
|
18923
19306
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
18924
|
-
import
|
|
18925
|
-
var
|
|
19307
|
+
import path21 from "node:path";
|
|
19308
|
+
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
18926
19309
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
18927
19310
|
return { ok: false, error: "hook_event_name is required" };
|
|
18928
19311
|
const roots = value.workspace_roots;
|
|
@@ -18969,7 +19352,7 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
18969
19352
|
return isWriteTool(input.tool_name) ? {
|
|
18970
19353
|
kind: "write.pre",
|
|
18971
19354
|
tool: input.tool_name ?? "",
|
|
18972
|
-
paths:
|
|
19355
|
+
paths: writeTargets(input.tool_input),
|
|
18973
19356
|
toolUseId: null
|
|
18974
19357
|
} : { kind: "tool.pre", tool: input.tool_name ?? "", toolUseId: null };
|
|
18975
19358
|
case "subagentStart":
|
|
@@ -18990,7 +19373,7 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
18990
19373
|
case "preCompact":
|
|
18991
19374
|
return { kind: "compact.pre", trigger: "auto" };
|
|
18992
19375
|
}
|
|
18993
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
19376
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path21.sep) ? root : `${root}${path21.sep}`), hookCwd = (input) => {
|
|
18994
19377
|
const cwd = existingDirectory(input.cwd);
|
|
18995
19378
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
18996
19379
|
return cwd;
|
|
@@ -19021,7 +19404,7 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
19021
19404
|
}, cursorAdapter;
|
|
19022
19405
|
var init_cursor = __esm(() => {
|
|
19023
19406
|
init_fields();
|
|
19024
|
-
|
|
19407
|
+
undocumented = { support: "undocumented", native: null };
|
|
19025
19408
|
none = { support: "none", native: null };
|
|
19026
19409
|
CURSOR_DESCRIPTOR = {
|
|
19027
19410
|
host: "cursor",
|
|
@@ -19035,26 +19418,28 @@ var init_cursor = __esm(() => {
|
|
|
19035
19418
|
"shell.pre": { support: "native", native: "beforeShellExecution" },
|
|
19036
19419
|
"tool.pre": { support: "native", native: "preToolUse" },
|
|
19037
19420
|
"write.pre": { support: "partial", native: "preToolUse" },
|
|
19038
|
-
"shell.post":
|
|
19421
|
+
"shell.post": undocumented,
|
|
19039
19422
|
"subagent.start": { support: "native", native: "subagentStart" },
|
|
19040
19423
|
"subagent.stop": { support: "partial", native: "subagentStop" },
|
|
19041
|
-
"prompt.submit":
|
|
19424
|
+
"prompt.submit": undocumented,
|
|
19042
19425
|
"compact.pre": { support: "partial", native: "preCompact" },
|
|
19043
|
-
stop:
|
|
19426
|
+
stop: none
|
|
19044
19427
|
},
|
|
19045
19428
|
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: true },
|
|
19046
19429
|
context: {
|
|
19047
19430
|
sessionStart: "native",
|
|
19048
19431
|
perTurn: "none",
|
|
19049
19432
|
afterCompact: "partial",
|
|
19050
|
-
task: "single-active"
|
|
19433
|
+
task: "single-active",
|
|
19434
|
+
turnResend: "every-turn"
|
|
19051
19435
|
},
|
|
19052
19436
|
subagents: {
|
|
19053
19437
|
identity: "native",
|
|
19054
19438
|
parentBinding: "native",
|
|
19055
19439
|
blockStart: "native",
|
|
19056
19440
|
worktreeIsolation: "undocumented",
|
|
19057
|
-
maxConcurrency: "undocumented"
|
|
19441
|
+
maxConcurrency: "undocumented",
|
|
19442
|
+
agentPrefix: "workit-"
|
|
19058
19443
|
},
|
|
19059
19444
|
provenance: {
|
|
19060
19445
|
sessionId: "native",
|
|
@@ -19211,6 +19596,7 @@ var init_hooks = __esm(async () => {
|
|
|
19211
19596
|
init_policy();
|
|
19212
19597
|
init_write_gate();
|
|
19213
19598
|
init_raw_git();
|
|
19599
|
+
init_skill_nudge();
|
|
19214
19600
|
init_fields();
|
|
19215
19601
|
init_handle();
|
|
19216
19602
|
init_claude_code();
|