@brainervirus/workit-claude-code 8.16.0 → 8.17.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "workit",
3
3
  "displayName": "Workit",
4
- "version": "8.16.0",
4
+ "version": "8.17.0",
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"
@@ -13,6 +13,7 @@
13
13
  // policy): a broken hook must never brick the host.
14
14
  import { spawnSync } from "node:child_process";
15
15
  import { existsSync, readFileSync, statSync } from "node:fs";
16
+ import os from "node:os";
16
17
  import path from "node:path";
17
18
  import { fileURLToPath, pathToFileURL } from "node:url";
18
19
 
@@ -37,7 +38,9 @@ const failOpen = (reason) => {
37
38
  // no redirect and no writing verb can need none of them; with no Workit task
38
39
  // store for the checkout there is no task to gate a non-git edit on; and a
39
40
  // PostToolUse only matters after a `git commit`. A workit delivery verb
40
- // (`workit pr create`, `workit ci wait`) may get the workit-ship nudge.
41
+ // (`workit pr create`, `workit ci wait`) may get the workit-ship nudge. A
42
+ // command that may be heavy (a catalog word or a pattern's first word, the
43
+ // copy of core/heavy-words.ts below) goes to the heavy-command gate.
41
44
  let payload = "";
42
45
  for await (const chunk of process.stdin) payload += String(chunk);
43
46
  const RAW_TOOLS = /\b(?:git|gh|glab)\b/;
@@ -46,6 +49,74 @@ const WORKIT_DELIVERY =
46
49
  const MAYBE_GATED =
47
50
  /\b(?:git|gh|glab)\b|>|\b(?:tee|touch|mkdir|rm|rmdir|mv|cp|truncate|install|ln|patch|dd|sed|perl|set-content|add-content|out-file|new-item|ni|remove-item|del|copy-item|move-item)\b/i;
48
51
  const SHELLS = new Set(["Bash", "PowerShell"]);
52
+ const HEAVY_WORDS = new Set(
53
+ (
54
+ "npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " +
55
+ "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " +
56
+ "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " +
57
+ "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " +
58
+ "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " +
59
+ "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " +
60
+ "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " +
61
+ "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " +
62
+ "ninja meson ctest scons docker-compose buildah earthly"
63
+ ).split(" "),
64
+ );
65
+ const wordsOf = (command) =>
66
+ command
67
+ .split(/[\s;&|()<>`'"]+/u)
68
+ .map((word) =>
69
+ (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, ""),
70
+ )
71
+ .filter(Boolean);
72
+ const readJson = (file) => {
73
+ try {
74
+ return JSON.parse(readFileSync(file, "utf8"));
75
+ } catch {
76
+ return undefined;
77
+ }
78
+ };
79
+ const stateDir = () => {
80
+ const env = process.env;
81
+ if (env.WORKFLOW_TOOLKIT_STATE) return env.WORKFLOW_TOOLKIT_STATE;
82
+ if (env.XDG_STATE_HOME) return path.join(env.XDG_STATE_HOME, "workit");
83
+ const home = env.HOME || os.homedir();
84
+ if (process.platform === "darwin")
85
+ return path.join(home, "Library", "Application Support", "workit");
86
+ if (process.platform === "win32")
87
+ return path.join(env.LOCALAPPDATA ?? path.join(home, "AppData", "Local"), "workit");
88
+ return path.join(home, ".local", "state", "workit");
89
+ };
90
+ const REPO_CHECKS = "workit.checks.json";
91
+ /** Could the command be heavy? A doubt answers yes (it only costs a runtime load). */
92
+ const maybeHeavy = (command, cwd) => {
93
+ try {
94
+ const words = wordsOf(command);
95
+ if (words.some((word) => HEAVY_WORDS.has(word) || /^python[\d.]*$/u.test(word))) return true;
96
+ const patterns = [];
97
+ const machine = readJson(path.join(stateDir(), "heavy-commands.json"));
98
+ if (Array.isArray(machine?.patterns)) patterns.push(...machine.patterns);
99
+ // The repo's checks config at the git top: keep REPO_CHECKS equal to
100
+ // checksConfigFile in workit-core/src/check-config.ts (a test holds them).
101
+ for (let dir = path.resolve(cwd); ; dir = path.dirname(dir)) {
102
+ if (existsSync(path.join(dir, ".git"))) {
103
+ const repo = readJson(path.join(dir, REPO_CHECKS));
104
+ if (Array.isArray(repo?.resources?.heavyCommands))
105
+ patterns.push(...repo.resources.heavyCommands);
106
+ break;
107
+ }
108
+ if (path.dirname(dir) === dir) break;
109
+ }
110
+ for (const pattern of patterns) {
111
+ if (typeof pattern !== "string") continue;
112
+ const first = wordsOf(pattern)[0];
113
+ if (!first || first.includes("*") || words.includes(first)) return true;
114
+ }
115
+ return false;
116
+ } catch {
117
+ return true;
118
+ }
119
+ };
49
120
  const EDITS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
50
121
 
51
122
  /** `<git-common-dir>/workit` (or `<dir>/.workit` outside git) holds tasks. */
@@ -85,6 +156,7 @@ const fastAllow = (() => {
85
156
  if (value?.hook_event_name !== "PreToolUse") return false;
86
157
  if (SHELLS.has(value.tool_name) && typeof command === "string") {
87
158
  if (WORKIT_DELIVERY.test(command)) return false;
159
+ if (maybeHeavy(command, String(value.cwd ?? "."))) return false;
88
160
  if (!MAYBE_GATED.test(command)) return true;
89
161
  return !RAW_TOOLS.test(command) && !hasTaskStore(String(value.cwd ?? "."));
90
162
  }