@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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "workit",
3
3
  "displayName": "Workit",
4
- "version": "8.27.0",
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"
@@ -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) {