@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
|
@@ -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,291 @@ 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
|
+
if (words.some((word) => /^(?:-i(?:\.\w+)?|--in-place(?:=.*)?)$/.test(word)))
|
|
18476
|
+
continue;
|
|
18477
|
+
for (const word of words.slice(1)) {
|
|
18478
|
+
const skill = skillFileIn(word);
|
|
18479
|
+
if (skill)
|
|
18480
|
+
return skill;
|
|
18481
|
+
}
|
|
18482
|
+
}
|
|
18483
|
+
return null;
|
|
18484
|
+
}, nudgeOnce = (input, session, skill, line) => {
|
|
18485
|
+
const file = markerFile2(input, session);
|
|
18486
|
+
if (!file)
|
|
18487
|
+
return null;
|
|
18488
|
+
const marker = readMarker(file);
|
|
18489
|
+
if (marker.nudged.includes(skill) || marker.loaded.includes(skill))
|
|
18490
|
+
return null;
|
|
18491
|
+
if (loadedSkills(input.cwd, session).has(skill))
|
|
18492
|
+
return null;
|
|
18493
|
+
writeMarker(file, { ...marker, nudged: [...marker.nudged, skill] });
|
|
18494
|
+
return line;
|
|
18495
|
+
}, 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 }) => {
|
|
18496
|
+
const at = words.findIndex((word) => !/^\w+=/.test(word) && !LAUNCHERS.has(word));
|
|
18497
|
+
if (at >= 0 && /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/.test(words[at])) {
|
|
18498
|
+
const args = words.slice(at + 1);
|
|
18499
|
+
if (args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run"))
|
|
18500
|
+
return false;
|
|
18501
|
+
return WORKIT_DELIVERY.test(args.join(" "));
|
|
18502
|
+
}
|
|
18503
|
+
const shell = SHELLS2.has(words[at]?.split(/[\\/]/).at(-1) ?? "");
|
|
18504
|
+
const script = shell && words[at + 1] === "-c" ? words[at + 2] : undefined;
|
|
18505
|
+
return depth < 3 && script !== undefined && runsWorkitDelivery(script, "posix", depth + 1);
|
|
18506
|
+
}), isDeliveryCommand = (cwd, command, dialect = "posix") => rawDelivery(cwd, command, dialect) || runsWorkitDelivery(command, dialect) && inWorkitWorkspace(cwd), withContextLine = (decision, line) => {
|
|
18507
|
+
if (!line)
|
|
18508
|
+
return decision;
|
|
18509
|
+
if (decision.kind === "none")
|
|
18510
|
+
return { kind: "context", text: line };
|
|
18511
|
+
if (decision.kind === "context")
|
|
18512
|
+
return { kind: "context", text: `${decision.text}
|
|
18513
|
+
${line}` };
|
|
18514
|
+
return decision;
|
|
18515
|
+
};
|
|
18516
|
+
var init_skill_nudge = __esm(() => {
|
|
18517
|
+
init_skill_manifests();
|
|
18518
|
+
init_ledger();
|
|
18519
|
+
init_paths();
|
|
18520
|
+
init_raw_git();
|
|
18521
|
+
USER_INVOKED = new Set(["workit-retro", "workit-architecture"]);
|
|
18522
|
+
SKILL_HOST = {
|
|
18523
|
+
claude_code: "claude-code",
|
|
18524
|
+
opencode: "opencode",
|
|
18525
|
+
codex_cli: "codex",
|
|
18526
|
+
codex_desktop: "codex",
|
|
18527
|
+
cursor: "cursor",
|
|
18528
|
+
pi: "pi"
|
|
18529
|
+
};
|
|
18530
|
+
PROMPT_INTENTS = [
|
|
18531
|
+
[
|
|
18532
|
+
"workit-debug",
|
|
18533
|
+
/\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
|
|
18534
|
+
],
|
|
18535
|
+
[
|
|
18536
|
+
"workit-continue",
|
|
18537
|
+
/\b(pick up where|where (?:were|did) we|resume (?:the|this|my|our|work|where)|hand ?off)\b/i
|
|
18538
|
+
],
|
|
18539
|
+
[
|
|
18540
|
+
"workit-review",
|
|
18541
|
+
/\b(blast radius|code review|review (?:the|this|my|PR|pr|#\d+|branch|diff|changes))\b/i
|
|
18542
|
+
],
|
|
18543
|
+
[
|
|
18544
|
+
"workit-ship",
|
|
18545
|
+
/\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
|
|
18546
|
+
],
|
|
18547
|
+
["workit-bdd", /\b(BDD|TDD|acceptance criteria|Given\/When\/Then)(?![\w/])/],
|
|
18548
|
+
["workit-test-audit", /\b(test audit|audit the tests|tautolog\w*|weak tests)\b/i],
|
|
18549
|
+
["workit-deslop", /\b(deslop|slop|dead code)\b/i],
|
|
18550
|
+
[
|
|
18551
|
+
"workit-fanout",
|
|
18552
|
+
/\b(fan(?: |-)?out|parallelize|parallel agents|agent swarm|swarm of agents)\b/i
|
|
18553
|
+
],
|
|
18554
|
+
["workit-verify-app", /\b(verify the app|smoke test|prove it works)\b/i],
|
|
18555
|
+
["workit-retro", /\b((?:a|the) retro(?![-\w]|\.\w)|retrospective)/i],
|
|
18556
|
+
["workit-architecture", /\b(deepen (?:the )?modules|architecture (?:review|pass|audit))\b/i],
|
|
18557
|
+
[
|
|
18558
|
+
"workit-shape",
|
|
18559
|
+
/\b(brainstorm\w*|grill me|should we|let'?s plan|plan (?:out|how|the|a|for)|(?:write|draft) (?:a|the) spec)\b/i
|
|
18560
|
+
],
|
|
18561
|
+
[
|
|
18562
|
+
"workit-implement",
|
|
18563
|
+
/\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
|
|
18564
|
+
]
|
|
18565
|
+
];
|
|
18566
|
+
SKILL_FILE = /(?:^|[\\/])(workit-[a-z-]+)[\\/]SKILL\.md$/;
|
|
18567
|
+
READERS = new Set([
|
|
18568
|
+
"cat",
|
|
18569
|
+
"sed",
|
|
18570
|
+
"head",
|
|
18571
|
+
"tail",
|
|
18572
|
+
"less",
|
|
18573
|
+
"more",
|
|
18574
|
+
"bat",
|
|
18575
|
+
"nl",
|
|
18576
|
+
"get-content",
|
|
18577
|
+
"gc",
|
|
18578
|
+
"type"
|
|
18579
|
+
]);
|
|
18580
|
+
WORKIT_DELIVERY = /^(?:pr (?:create|merge)|ci wait|git push)\b/;
|
|
18581
|
+
SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
18582
|
+
LAUNCHERS = new Set([
|
|
18583
|
+
"npx",
|
|
18584
|
+
"bunx",
|
|
18585
|
+
"pnpx",
|
|
18586
|
+
"-y",
|
|
18587
|
+
"--yes",
|
|
18588
|
+
"pnpm",
|
|
18589
|
+
"dlx",
|
|
18590
|
+
"exec",
|
|
18591
|
+
"env",
|
|
18592
|
+
"sudo",
|
|
18593
|
+
"command"
|
|
18594
|
+
]);
|
|
18595
|
+
});
|
|
18596
|
+
|
|
18256
18597
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18257
18598
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
18258
|
-
import
|
|
18599
|
+
import path20 from "node:path";
|
|
18259
18600
|
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) || !
|
|
18601
|
+
if (!nonEmpty2(value) || !path20.isAbsolute(value) || !existsSync6(value))
|
|
18261
18602
|
return null;
|
|
18262
18603
|
try {
|
|
18263
18604
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
18264
18605
|
} catch {
|
|
18265
18606
|
return null;
|
|
18266
18607
|
}
|
|
18267
|
-
},
|
|
18608
|
+
}, SHELLS3, SCRIPT_FLAG2, quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg), commandText = (value) => {
|
|
18268
18609
|
if (nonEmpty2(value))
|
|
18269
18610
|
return value;
|
|
18270
18611
|
if (!Array.isArray(value) || value.length === 0)
|
|
@@ -18273,7 +18614,7 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
18273
18614
|
if (!argv.every((arg) => typeof arg === "string"))
|
|
18274
18615
|
return null;
|
|
18275
18616
|
const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
|
|
18276
|
-
if (
|
|
18617
|
+
if (SHELLS3.has(shell))
|
|
18277
18618
|
for (let index = 1;index < argv.length - 1; index++) {
|
|
18278
18619
|
if (SCRIPT_FLAG2.test(argv[index]))
|
|
18279
18620
|
return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
|
|
@@ -18282,18 +18623,21 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
18282
18623
|
}
|
|
18283
18624
|
const joined = argv.map(quoteArg).join(" ");
|
|
18284
18625
|
return nonEmpty2(joined) ? joined : null;
|
|
18285
|
-
}, WRITE_TOOLS, isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, "")), PATH_KEYS, writePaths = (input) => {
|
|
18626
|
+
}, WRITE_TOOLS, isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, "")), PATH_KEYS, PATCH_HEADER, writePaths = (input) => {
|
|
18286
18627
|
if (!isRecord4(input))
|
|
18287
18628
|
return [];
|
|
18288
18629
|
const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
|
|
18289
18630
|
for (const value of Object.values(input))
|
|
18290
18631
|
if (typeof value === "string")
|
|
18291
|
-
for (const match of value.matchAll(
|
|
18632
|
+
for (const match of value.matchAll(PATCH_HEADER))
|
|
18292
18633
|
paths.push(match[1].trim());
|
|
18293
18634
|
return [...new Set(paths)];
|
|
18635
|
+
}, writeTargets = (input) => {
|
|
18636
|
+
const paths = writePaths(input);
|
|
18637
|
+
return paths.length > 0 ? paths : null;
|
|
18294
18638
|
};
|
|
18295
18639
|
var init_fields = __esm(() => {
|
|
18296
|
-
|
|
18640
|
+
SHELLS3 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
18297
18641
|
SCRIPT_FLAG2 = /^(?:-\w*c|-command|\/c)$/i;
|
|
18298
18642
|
WRITE_TOOLS = new Set([
|
|
18299
18643
|
"write",
|
|
@@ -18315,6 +18659,7 @@ var init_fields = __esm(() => {
|
|
|
18315
18659
|
"writefile"
|
|
18316
18660
|
]);
|
|
18317
18661
|
PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
|
|
18662
|
+
PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
|
|
18318
18663
|
});
|
|
18319
18664
|
|
|
18320
18665
|
// packages/workit-core/src/hooks/handle.ts
|
|
@@ -18332,18 +18677,22 @@ function handleHook(input, deps) {
|
|
|
18332
18677
|
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
18678
|
const addendum = [deps.addendum, advisory].filter(Boolean).join(`
|
|
18334
18679
|
`) || null;
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
|
|
18338
|
-
};
|
|
18680
|
+
const text = sessionContextText(input, descriptor, { offer, addendum });
|
|
18681
|
+
if (resendsOnChange(descriptor))
|
|
18682
|
+
seedTurnContext(input, descriptor);
|
|
18683
|
+
return { kind: "context", text };
|
|
18339
18684
|
}
|
|
18340
18685
|
case "context.turn": {
|
|
18341
18686
|
if (!usable2(descriptor.context.perTurn))
|
|
18342
18687
|
return NONE4;
|
|
18343
|
-
const
|
|
18344
|
-
|
|
18688
|
+
const current = turnContextText(input, descriptor);
|
|
18689
|
+
const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
|
|
18690
|
+
return withContextLine(text ? { kind: "context", text } : NONE4, promptNudge(input, event.prompt));
|
|
18345
18691
|
}
|
|
18346
18692
|
case "shell.pre": {
|
|
18693
|
+
const skillRead = skillReadIn(event.command, event.dialect);
|
|
18694
|
+
if (skillRead)
|
|
18695
|
+
recordSkillLoad(input, skillRead, "read");
|
|
18347
18696
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
18348
18697
|
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE4;
|
|
18349
18698
|
if (policy.kind !== "none")
|
|
@@ -18359,7 +18708,7 @@ function handleHook(input, deps) {
|
|
|
18359
18708
|
if (gate.kind === "deny")
|
|
18360
18709
|
return gate;
|
|
18361
18710
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
18362
|
-
return raw;
|
|
18711
|
+
return withContextLine(raw, shipNudge(input, event.command));
|
|
18363
18712
|
}
|
|
18364
18713
|
case "shell.post":
|
|
18365
18714
|
if (usable2(descriptor.events["shell.post"].support))
|
|
@@ -18370,7 +18719,7 @@ function handleHook(input, deps) {
|
|
|
18370
18719
|
case "subagent.start":
|
|
18371
18720
|
return {
|
|
18372
18721
|
kind: "context",
|
|
18373
|
-
text: subagentStartText(input.
|
|
18722
|
+
text: subagentStartText(input.session.id ?? null, descriptor, event)
|
|
18374
18723
|
};
|
|
18375
18724
|
case "compact.pre":
|
|
18376
18725
|
return {
|
|
@@ -18378,19 +18727,31 @@ function handleHook(input, deps) {
|
|
|
18378
18727
|
userMessage: "Workit context may be stale after compaction; re-run inspection or resume before acting."
|
|
18379
18728
|
};
|
|
18380
18729
|
case "tool.pre":
|
|
18730
|
+
if (event.skill)
|
|
18731
|
+
recordSkillLoad(input, event.skill, "tool");
|
|
18732
|
+
return NONE4;
|
|
18381
18733
|
case "subagent.stop":
|
|
18382
18734
|
case "prompt.submit":
|
|
18383
18735
|
case "stop":
|
|
18384
18736
|
return NONE4;
|
|
18385
18737
|
}
|
|
18386
18738
|
}
|
|
18387
|
-
var usable2 = (value) => value === "native" || value === "partial", NONE4,
|
|
18388
|
-
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18739
|
+
var usable2 = (value) => value === "native" || value === "partial", NONE4, resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn), offered, ROLES, workitRole = (descriptor, agentType) => {
|
|
18740
|
+
const prefix = descriptor.subagents.agentPrefix;
|
|
18741
|
+
const name = agentType.startsWith(prefix) ? agentType.slice(prefix.length) : "";
|
|
18742
|
+
return Object.hasOwn(ROLES, name) ? ROLES[name] : null;
|
|
18743
|
+
}, subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128), subagentStartText = (session, descriptor, event) => {
|
|
18744
|
+
const role = workitRole(descriptor, event.agentType);
|
|
18745
|
+
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
18746
|
+
if (role === "implementer") {
|
|
18747
|
+
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";
|
|
18748
|
+
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.`;
|
|
18749
|
+
}
|
|
18750
|
+
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
18751
|
+
if (role !== "judge" || !event.agentId)
|
|
18752
|
+
return readOnly;
|
|
18753
|
+
const own = subagentSession(session, event.agentId);
|
|
18754
|
+
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
18755
|
}, failureDecision = (descriptor, event, error) => {
|
|
18395
18756
|
if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
|
|
18396
18757
|
return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE4;
|
|
@@ -18423,10 +18784,15 @@ var init_handle = __esm(() => {
|
|
|
18423
18784
|
init_context();
|
|
18424
18785
|
init_policy();
|
|
18425
18786
|
init_raw_git();
|
|
18787
|
+
init_skill_nudge();
|
|
18426
18788
|
init_write_gate();
|
|
18427
18789
|
NONE4 = { kind: "none" };
|
|
18428
18790
|
offered = new Set;
|
|
18429
|
-
|
|
18791
|
+
ROLES = {
|
|
18792
|
+
implementer: "implementer",
|
|
18793
|
+
verifier: "judge",
|
|
18794
|
+
reviewer: "judge"
|
|
18795
|
+
};
|
|
18430
18796
|
});
|
|
18431
18797
|
|
|
18432
18798
|
// packages/workit-core/src/hooks/hosts/claude-code.ts
|
|
@@ -18436,7 +18802,7 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
18436
18802
|
case "SessionStart":
|
|
18437
18803
|
return SOURCES.has(value.source) ? { ok: true, event: { kind: "session.start", source: value.source } } : { ok: false, error: "SessionStart source is required" };
|
|
18438
18804
|
case "UserPromptSubmit":
|
|
18439
|
-
return { ok: true, event: { kind: "context.turn" } };
|
|
18805
|
+
return { ok: true, event: { kind: "context.turn", prompt: optionalText(value.prompt) } };
|
|
18440
18806
|
case "PreToolUse": {
|
|
18441
18807
|
if (!nonEmpty2(value.tool_name))
|
|
18442
18808
|
return { ok: false, error: "tool_name is required" };
|
|
@@ -18446,10 +18812,20 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
18446
18812
|
event: {
|
|
18447
18813
|
kind: "write.pre",
|
|
18448
18814
|
tool: value.tool_name,
|
|
18449
|
-
paths:
|
|
18815
|
+
paths: writeTargets(value.tool_input),
|
|
18450
18816
|
toolUseId
|
|
18451
18817
|
}
|
|
18452
18818
|
};
|
|
18819
|
+
if (value.tool_name === "Skill")
|
|
18820
|
+
return {
|
|
18821
|
+
ok: true,
|
|
18822
|
+
event: {
|
|
18823
|
+
kind: "tool.pre",
|
|
18824
|
+
tool: value.tool_name,
|
|
18825
|
+
toolUseId,
|
|
18826
|
+
skill: isRecord4(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
18827
|
+
}
|
|
18828
|
+
};
|
|
18453
18829
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
18454
18830
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
18455
18831
|
const command = toolCommand(value);
|
|
@@ -18547,24 +18923,26 @@ var init_claude_code = __esm(() => {
|
|
|
18547
18923
|
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18548
18924
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18549
18925
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18550
|
-
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18551
18926
|
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18552
|
-
"
|
|
18553
|
-
|
|
18927
|
+
"subagent.stop": { support: "none", native: null },
|
|
18928
|
+
"compact.pre": { support: "none", native: null },
|
|
18929
|
+
stop: { support: "none", native: null }
|
|
18554
18930
|
},
|
|
18555
18931
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18556
18932
|
context: {
|
|
18557
18933
|
sessionStart: "native",
|
|
18558
18934
|
perTurn: "native",
|
|
18559
18935
|
afterCompact: "native",
|
|
18560
|
-
task: "session-bound"
|
|
18936
|
+
task: "session-bound",
|
|
18937
|
+
turnResend: "every-turn"
|
|
18561
18938
|
},
|
|
18562
18939
|
subagents: {
|
|
18563
18940
|
identity: "native",
|
|
18564
18941
|
parentBinding: "partial",
|
|
18565
18942
|
blockStart: "none",
|
|
18566
18943
|
worktreeIsolation: "native",
|
|
18567
|
-
maxConcurrency: "undocumented"
|
|
18944
|
+
maxConcurrency: "undocumented",
|
|
18945
|
+
agentPrefix: "workit:"
|
|
18568
18946
|
},
|
|
18569
18947
|
provenance: { sessionId: "native", agentIdOnTool: "native", postToolObserve: "native" },
|
|
18570
18948
|
interaction: { questions: "undocumented", writeBoundary: "partial" },
|
|
@@ -18582,13 +18960,13 @@ var init_claude_code = __esm(() => {
|
|
|
18582
18960
|
},
|
|
18583
18961
|
{
|
|
18584
18962
|
name: "native_subagents",
|
|
18585
|
-
surface: "SubagentStart
|
|
18586
|
-
refs: ["SubagentStart"
|
|
18587
|
-
requires: ["event:subagent.start"
|
|
18588
|
-
observed: ["subagent.start"
|
|
18963
|
+
surface: "SubagentStart",
|
|
18964
|
+
refs: ["SubagentStart"],
|
|
18965
|
+
requires: ["event:subagent.start"],
|
|
18966
|
+
observed: ["subagent.start"],
|
|
18589
18967
|
assurance: "agent_guided",
|
|
18590
18968
|
reason: "Claude Code reports stable agent identities, but SubagentStart cannot block or bind",
|
|
18591
|
-
unavailableReason: "Claude Code
|
|
18969
|
+
unavailableReason: "Claude Code SubagentStart is unavailable"
|
|
18592
18970
|
},
|
|
18593
18971
|
{
|
|
18594
18972
|
name: "fresh-context-review",
|
|
@@ -18684,7 +19062,7 @@ var init_claude_code = __esm(() => {
|
|
|
18684
19062
|
function detectCodexSurface(env) {
|
|
18685
19063
|
return env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || Boolean(env.CODEX_ELECTRON_RESOURCES_PATH) ? "codex_desktop" : "codex_cli";
|
|
18686
19064
|
}
|
|
18687
|
-
var
|
|
19065
|
+
var CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase()), SOURCES2, parseCodexHookInput = (value) => {
|
|
18688
19066
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
|
|
18689
19067
|
return { ok: false, error: "hook_event_name is required" };
|
|
18690
19068
|
const event = value.hook_event_name;
|
|
@@ -18710,6 +19088,7 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
18710
19088
|
cwd,
|
|
18711
19089
|
...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
|
|
18712
19090
|
...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
|
|
19091
|
+
...event === "UserPromptSubmit" ? { prompt: optionalText(value.prompt) } : {},
|
|
18713
19092
|
...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
|
|
18714
19093
|
...toolEvent ? { tool_input: value.tool_input } : {},
|
|
18715
19094
|
...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
|
|
@@ -18728,13 +19107,20 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
18728
19107
|
switch (input.hook_event_name) {
|
|
18729
19108
|
case "SessionStart":
|
|
18730
19109
|
return { kind: "session.start", source: input.source };
|
|
19110
|
+
case "UserPromptSubmit":
|
|
19111
|
+
return { kind: "context.turn", prompt: input.prompt ?? null };
|
|
18731
19112
|
case "PreToolUse": {
|
|
18732
19113
|
const toolUseId = input.tool_use_id ?? null;
|
|
18733
|
-
|
|
18734
|
-
kind: "shell.pre",
|
|
18735
|
-
|
|
18736
|
-
|
|
18737
|
-
|
|
19114
|
+
if (isShellTool(input.tool_name))
|
|
19115
|
+
return { kind: "shell.pre", command: input.command ?? "", toolUseId };
|
|
19116
|
+
if (isWriteTool(input.tool_name))
|
|
19117
|
+
return {
|
|
19118
|
+
kind: "write.pre",
|
|
19119
|
+
tool: input.tool_name,
|
|
19120
|
+
paths: writeTargets(input.tool_input),
|
|
19121
|
+
toolUseId
|
|
19122
|
+
};
|
|
19123
|
+
return { kind: "tool.pre", tool: input.tool_name, toolUseId };
|
|
18738
19124
|
}
|
|
18739
19125
|
case "PostToolUse": {
|
|
18740
19126
|
const toolUseId = input.tool_use_id ?? null;
|
|
@@ -18781,39 +19167,40 @@ var undocumented, CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name)
|
|
|
18781
19167
|
}, codexAdapter;
|
|
18782
19168
|
var init_codex = __esm(() => {
|
|
18783
19169
|
init_fields();
|
|
18784
|
-
undocumented = { support: "undocumented", native: null };
|
|
18785
19170
|
CODEX_DESCRIPTOR = {
|
|
18786
19171
|
host: "codex_cli",
|
|
18787
19172
|
label: "Codex",
|
|
18788
|
-
verifiedAgainst: "codex-cli 0.
|
|
19173
|
+
verifiedAgainst: "codex-cli 0.160.1 (hook JSON schemas in the binary); Codex Desktop 26.901.20858",
|
|
18789
19174
|
docs: ["https://developers.openai.com/codex/hooks"],
|
|
18790
19175
|
transport: "hook-process",
|
|
18791
19176
|
events: {
|
|
18792
19177
|
"session.start": { support: "native", native: "SessionStart" },
|
|
18793
|
-
"context.turn":
|
|
19178
|
+
"context.turn": { support: "native", native: "UserPromptSubmit" },
|
|
18794
19179
|
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
18795
19180
|
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
18796
|
-
"write.pre": { support: "
|
|
19181
|
+
"write.pre": { support: "native", native: "PreToolUse" },
|
|
18797
19182
|
"shell.post": { support: "native", native: "PostToolUse" },
|
|
18798
19183
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
18799
19184
|
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
18800
|
-
"prompt.submit":
|
|
19185
|
+
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
18801
19186
|
"compact.pre": { support: "none", native: null },
|
|
18802
|
-
stop:
|
|
19187
|
+
stop: { support: "none", native: null }
|
|
18803
19188
|
},
|
|
18804
19189
|
shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
|
|
18805
19190
|
context: {
|
|
18806
19191
|
sessionStart: "native",
|
|
18807
|
-
perTurn: "
|
|
19192
|
+
perTurn: "native",
|
|
18808
19193
|
afterCompact: "native",
|
|
18809
|
-
task: "single-active"
|
|
19194
|
+
task: "single-active",
|
|
19195
|
+
turnResend: "on-change"
|
|
18810
19196
|
},
|
|
18811
19197
|
subagents: {
|
|
18812
19198
|
identity: "native",
|
|
18813
19199
|
parentBinding: "none",
|
|
18814
19200
|
blockStart: "none",
|
|
18815
19201
|
worktreeIsolation: "undocumented",
|
|
18816
|
-
maxConcurrency: "undocumented"
|
|
19202
|
+
maxConcurrency: "undocumented",
|
|
19203
|
+
agentPrefix: "workit-"
|
|
18817
19204
|
},
|
|
18818
19205
|
provenance: { sessionId: "native", agentIdOnTool: "partial", postToolObserve: "native" },
|
|
18819
19206
|
interaction: { questions: "none", writeBoundary: "partial" },
|
|
@@ -18880,13 +19267,14 @@ var init_codex = __esm(() => {
|
|
|
18880
19267
|
};
|
|
18881
19268
|
EVENTS2 = {
|
|
18882
19269
|
SessionStart: "session.start",
|
|
19270
|
+
UserPromptSubmit: "context.turn",
|
|
18883
19271
|
PreToolUse: "shell.pre",
|
|
18884
19272
|
PostToolUse: "shell.post",
|
|
18885
19273
|
SubagentStart: "subagent.start",
|
|
18886
19274
|
SubagentStop: "subagent.stop"
|
|
18887
19275
|
};
|
|
18888
19276
|
SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
|
|
18889
|
-
SOURCES2 = new Set(["startup", "resume", "clear", "compact"]);
|
|
19277
|
+
SOURCES2 = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
18890
19278
|
codexAdapter = {
|
|
18891
19279
|
descriptor: CODEX_DESCRIPTOR,
|
|
18892
19280
|
parse(raw, env) {
|
|
@@ -18916,13 +19304,13 @@ var init_codex = __esm(() => {
|
|
|
18916
19304
|
};
|
|
18917
19305
|
},
|
|
18918
19306
|
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>)
|
|
19307
|
+
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
19308
|
};
|
|
18921
19309
|
});
|
|
18922
19310
|
|
|
18923
19311
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
18924
|
-
import
|
|
18925
|
-
var
|
|
19312
|
+
import path21 from "node:path";
|
|
19313
|
+
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
18926
19314
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
18927
19315
|
return { ok: false, error: "hook_event_name is required" };
|
|
18928
19316
|
const roots = value.workspace_roots;
|
|
@@ -18969,7 +19357,7 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
18969
19357
|
return isWriteTool(input.tool_name) ? {
|
|
18970
19358
|
kind: "write.pre",
|
|
18971
19359
|
tool: input.tool_name ?? "",
|
|
18972
|
-
paths:
|
|
19360
|
+
paths: writeTargets(input.tool_input),
|
|
18973
19361
|
toolUseId: null
|
|
18974
19362
|
} : { kind: "tool.pre", tool: input.tool_name ?? "", toolUseId: null };
|
|
18975
19363
|
case "subagentStart":
|
|
@@ -18990,7 +19378,7 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
18990
19378
|
case "preCompact":
|
|
18991
19379
|
return { kind: "compact.pre", trigger: "auto" };
|
|
18992
19380
|
}
|
|
18993
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
19381
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path21.sep) ? root : `${root}${path21.sep}`), hookCwd = (input) => {
|
|
18994
19382
|
const cwd = existingDirectory(input.cwd);
|
|
18995
19383
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
18996
19384
|
return cwd;
|
|
@@ -19021,7 +19409,7 @@ var undocumented2, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (val
|
|
|
19021
19409
|
}, cursorAdapter;
|
|
19022
19410
|
var init_cursor = __esm(() => {
|
|
19023
19411
|
init_fields();
|
|
19024
|
-
|
|
19412
|
+
undocumented = { support: "undocumented", native: null };
|
|
19025
19413
|
none = { support: "none", native: null };
|
|
19026
19414
|
CURSOR_DESCRIPTOR = {
|
|
19027
19415
|
host: "cursor",
|
|
@@ -19035,26 +19423,28 @@ var init_cursor = __esm(() => {
|
|
|
19035
19423
|
"shell.pre": { support: "native", native: "beforeShellExecution" },
|
|
19036
19424
|
"tool.pre": { support: "native", native: "preToolUse" },
|
|
19037
19425
|
"write.pre": { support: "partial", native: "preToolUse" },
|
|
19038
|
-
"shell.post":
|
|
19426
|
+
"shell.post": undocumented,
|
|
19039
19427
|
"subagent.start": { support: "native", native: "subagentStart" },
|
|
19040
19428
|
"subagent.stop": { support: "partial", native: "subagentStop" },
|
|
19041
|
-
"prompt.submit":
|
|
19429
|
+
"prompt.submit": undocumented,
|
|
19042
19430
|
"compact.pre": { support: "partial", native: "preCompact" },
|
|
19043
|
-
stop:
|
|
19431
|
+
stop: none
|
|
19044
19432
|
},
|
|
19045
19433
|
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: true },
|
|
19046
19434
|
context: {
|
|
19047
19435
|
sessionStart: "native",
|
|
19048
19436
|
perTurn: "none",
|
|
19049
19437
|
afterCompact: "partial",
|
|
19050
|
-
task: "single-active"
|
|
19438
|
+
task: "single-active",
|
|
19439
|
+
turnResend: "every-turn"
|
|
19051
19440
|
},
|
|
19052
19441
|
subagents: {
|
|
19053
19442
|
identity: "native",
|
|
19054
19443
|
parentBinding: "native",
|
|
19055
19444
|
blockStart: "native",
|
|
19056
19445
|
worktreeIsolation: "undocumented",
|
|
19057
|
-
maxConcurrency: "undocumented"
|
|
19446
|
+
maxConcurrency: "undocumented",
|
|
19447
|
+
agentPrefix: "workit-"
|
|
19058
19448
|
},
|
|
19059
19449
|
provenance: {
|
|
19060
19450
|
sessionId: "native",
|
|
@@ -19211,6 +19601,7 @@ var init_hooks = __esm(async () => {
|
|
|
19211
19601
|
init_policy();
|
|
19212
19602
|
init_write_gate();
|
|
19213
19603
|
init_raw_git();
|
|
19604
|
+
init_skill_nudge();
|
|
19214
19605
|
init_fields();
|
|
19215
19606
|
init_handle();
|
|
19216
19607
|
init_claude_code();
|