@brainervirus/workit-claude-code 8.27.0 → 8.28.1
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/.claude-plugin/plugin.json +1 -1
- package/bin/workit-hook.mjs +72 -0
- package/dist/workit-hook.js +85 -83
- package/dist/workit.js +333 -156
- package/hooks/hooks.json +12 -1
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workit",
|
|
3
3
|
"displayName": "Workit",
|
|
4
|
-
"version": "8.
|
|
4
|
+
"version": "8.28.1",
|
|
5
5
|
"description": "Workflow rails for agentic coding: task context on every session, branch policy on shell commands, and the workit method skills",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "BrainerVirus"
|
package/bin/workit-hook.mjs
CHANGED
|
@@ -33,6 +33,33 @@ const failOpen = (reason) => {
|
|
|
33
33
|
process.stdout.write("{}\n");
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
// A Node below this package's own engines minimum cannot load dist: fail open
|
|
37
|
+
// with one clear line instead of a syntax error. WORKIT_HOOK_NODE_VERSION
|
|
38
|
+
// stands in for process.versions.node (tests). An unreadable minimum never blocks.
|
|
39
|
+
const nodeFloorMessage = () => {
|
|
40
|
+
try {
|
|
41
|
+
const found = process.env.WORKIT_HOOK_NODE_VERSION || process.versions.node;
|
|
42
|
+
const range = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")).engines?.node;
|
|
43
|
+
const min = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(String(range ?? ""));
|
|
44
|
+
if (!min) return null;
|
|
45
|
+
const need = [min[1], min[2] ?? 0, min[3] ?? 0].map(Number);
|
|
46
|
+
const have = String(found).replace(/^v/, "").split(".").map(Number);
|
|
47
|
+
for (let i = 0; i < 3; i++) {
|
|
48
|
+
const a = have[i] ?? 0;
|
|
49
|
+
if (Number.isNaN(a)) return null;
|
|
50
|
+
if (a !== need[i]) return a < need[i] ? `needs Node ${range}, found ${found}` : null;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const tooOld = nodeFloorMessage();
|
|
58
|
+
if (tooOld) {
|
|
59
|
+
failOpen(tooOld);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
36
63
|
// Every Bash/PowerShell call and every file edit reaches this hook (branch
|
|
37
64
|
// policy, raw git/forge steering and the before-write gate). Fast paths
|
|
38
65
|
// answer `{}` before the runtime loads: a shell command with no git/gh/glab,
|
|
@@ -213,10 +240,51 @@ const standingPending = (cwd, session, agentId) => {
|
|
|
213
240
|
return readJson(path.join(store, "standing", "seen", `${key}.json`))?.rev !== rev;
|
|
214
241
|
};
|
|
215
242
|
|
|
243
|
+
// The plugin dir this hook runs from (`<config>/plugins/cache/<mkt>/workit/<version>`)
|
|
244
|
+
// against the newest version the registry lists beside it: while the line has not
|
|
245
|
+
// been given this session, the runtime runs (src/stale-plugin.ts holds the same test).
|
|
246
|
+
const parts = (version) => /^(\d+)\.(\d+)\.(\d+)/.exec(version)?.slice(1).map(Number);
|
|
247
|
+
const staleUnannounced = (session) => {
|
|
248
|
+
try {
|
|
249
|
+
const versions = path.dirname(root);
|
|
250
|
+
if (path.basename(path.dirname(path.dirname(versions))) !== "cache") return false;
|
|
251
|
+
const running = parts(path.basename(root));
|
|
252
|
+
if (!running || typeof session !== "string") return false;
|
|
253
|
+
const registry = readJson(
|
|
254
|
+
path.join(path.dirname(path.dirname(path.dirname(versions))), "installed_plugins.json"),
|
|
255
|
+
);
|
|
256
|
+
for (const entries of Object.values(registry?.plugins ?? {}))
|
|
257
|
+
for (const entry of Array.isArray(entries) ? entries : []) {
|
|
258
|
+
const at = typeof entry?.version === "string" ? parts(entry.version) : undefined;
|
|
259
|
+
if (
|
|
260
|
+
at &&
|
|
261
|
+
path.dirname(String(entry.installPath)) === versions &&
|
|
262
|
+
(at[0] - running[0] || at[1] - running[1] || at[2] - running[2]) > 0
|
|
263
|
+
)
|
|
264
|
+
return !existsSync(markerFor(session, entry.version));
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
// an unreadable registry is not a reason to load the runtime
|
|
268
|
+
}
|
|
269
|
+
return false;
|
|
270
|
+
};
|
|
271
|
+
const markerFor = (session, installed) =>
|
|
272
|
+
path.join(
|
|
273
|
+
stateDir(),
|
|
274
|
+
"stale-plugin",
|
|
275
|
+
createHash("sha256").update(`${session}\0${installed}`).digest("hex").slice(0, 32),
|
|
276
|
+
);
|
|
277
|
+
|
|
216
278
|
const fastAllow = (() => {
|
|
217
279
|
try {
|
|
218
280
|
const value = JSON.parse(payload);
|
|
219
281
|
const command = value?.tool_input?.command;
|
|
282
|
+
if (
|
|
283
|
+
value?.hook_event_name === "PreToolUse" &&
|
|
284
|
+
!value.agent_id &&
|
|
285
|
+
staleUnannounced(value.session_id)
|
|
286
|
+
)
|
|
287
|
+
return false;
|
|
220
288
|
if (value?.hook_event_name === "PostToolUse")
|
|
221
289
|
return typeof command === "string" && !/\bcommit\b/.test(command);
|
|
222
290
|
if (value?.hook_event_name !== "PreToolUse") return false;
|
|
@@ -233,12 +301,16 @@ const fastAllow = (() => {
|
|
|
233
301
|
return true;
|
|
234
302
|
return !implementer && !RAW_TOOLS.test(command) && !hasTaskStore(String(value.cwd ?? "."));
|
|
235
303
|
}
|
|
304
|
+
// A subagent spawn that names its model has no default to fill (hooks/agent-model.ts).
|
|
305
|
+
if (value.tool_name === "Agent" || value.tool_name === "Task")
|
|
306
|
+
return typeof value.tool_input?.model === "string" && value.tool_input.model.trim() !== "";
|
|
236
307
|
return EDITS.has(value.tool_name) && !implementer && !hasTaskStore(String(value.cwd ?? "."));
|
|
237
308
|
} catch {
|
|
238
309
|
return false;
|
|
239
310
|
}
|
|
240
311
|
})();
|
|
241
312
|
|
|
313
|
+
process.env.WORKIT_PLUGIN_ROOT = root;
|
|
242
314
|
if (fastAllow) {
|
|
243
315
|
process.stdout.write("{}\n");
|
|
244
316
|
} else if (fromSource) {
|