@pob/root 24.5.0 → 24.7.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.
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PostToolUse hook: runs lint-staged commands immediately on the edited file.
5
+ * Uses lint-staged.config.js as single source of truth.
6
+ */
7
+
8
+ import { spawnSync } from "node:child_process";
9
+ import { existsSync } from "node:fs";
10
+ import path from "node:path";
11
+ import { pathToFileURL } from "node:url";
12
+ import picomatch from "picomatch";
13
+
14
+ const chunks = [];
15
+ for await (const chunk of process.stdin) chunks.push(chunk);
16
+ const raw = chunks.join("");
17
+ if (!raw) {
18
+ process.stderr.write("No input received\n");
19
+ process.exit(1);
20
+ }
21
+ const input = JSON.parse(raw);
22
+
23
+ const file = input.tool_input?.file_path;
24
+ const cwd = input.cwd;
25
+ if (!file || !cwd) process.exit(0);
26
+
27
+ const relFile = path.relative(cwd, file);
28
+
29
+ // chdir before importing the config so path.resolve("package.json") and
30
+ // whichPmRuns()'s lockfile detection both resolve against the project root.
31
+ process.chdir(cwd);
32
+
33
+ const configPath = path.join(cwd, "lint-staged.config.js");
34
+ if (!existsSync(configPath)) process.exit(0);
35
+
36
+ const { default: config } = await import(pathToFileURL(configPath).href);
37
+
38
+ // Skip commands that are too slow for per-file runs or that have undesirable side-effects.
39
+ const SKIP = [
40
+ "git add", // side effects during agent execution
41
+ "tsc", // too slow per-file (matches "tsc" and "tsc -b")
42
+ "rollup", // too slow per-file
43
+ "yarn", // package manager install/dedupe
44
+ "pnpm", // package manager install/dedupe
45
+ "npm", // package manager install/dedupe
46
+ "bun", // package manager install/dedupe
47
+ ];
48
+
49
+ const shouldSkip = (cmd) =>
50
+ SKIP.some((s) => cmd === s || cmd.startsWith(`${s} `));
51
+
52
+ let hasError = false;
53
+
54
+ for (const [pattern, commandFnOrArray] of Object.entries(config)) {
55
+ // Replicate lint-staged matchBase: patterns without "/" match on basename only.
56
+ const matchBase = !pattern.includes("/");
57
+ if (!picomatch.isMatch(relFile, pattern, { matchBase })) continue;
58
+
59
+ let commands;
60
+ if (typeof commandFnOrArray === "function") {
61
+ const result = commandFnOrArray([file]);
62
+ commands = Array.isArray(result) ? result : [result];
63
+ } else {
64
+ const arr = Array.isArray(commandFnOrArray)
65
+ ? commandFnOrArray
66
+ : [commandFnOrArray];
67
+ commands = arr.map((cmd) => `${cmd} ${JSON.stringify(file)}`);
68
+ }
69
+
70
+ for (const cmd of commands.filter(Boolean)) {
71
+ if (shouldSkip(cmd)) continue;
72
+
73
+ const result = spawnSync(cmd, {
74
+ cwd,
75
+ shell: true,
76
+ stdio: "pipe",
77
+ env: {
78
+ ...process.env,
79
+ PATH: `${cwd}/node_modules/.bin:${process.env.PATH}`,
80
+ },
81
+ });
82
+
83
+ if (result.status !== 0) {
84
+ hasError = true;
85
+ const out = result.stdout?.toString().trim();
86
+ const err = result.stderr?.toString().trim();
87
+ if (out) process.stderr.write(`${out}\n`);
88
+ if (err) process.stderr.write(`${err}\n`);
89
+ }
90
+ }
91
+ }
92
+
93
+ // Exit 2 so Claude sees errors and fixes them before continuing.
94
+ process.exit(hasError ? 2 : 0);
@@ -1,3 +1,25 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ function detectFromLockfiles(cwd) {
5
+ if (existsSync(path.join(cwd, "yarn.lock"))) {
6
+ return { name: "yarn", version: "unknown" };
7
+ }
8
+ if (
9
+ existsSync(path.join(cwd, "pnpm-lock.yaml")) ||
10
+ existsSync(path.join(cwd, "pnpm-workspace.yaml"))
11
+ ) {
12
+ return { name: "pnpm", version: "unknown" };
13
+ }
14
+ if (existsSync(path.join(cwd, "bun.lock"))) {
15
+ return { name: "bun", version: "unknown" };
16
+ }
17
+ if (existsSync(path.join(cwd, "package-lock.json"))) {
18
+ return { name: "npm", version: "unknown" };
19
+ }
20
+ return undefined;
21
+ }
22
+
1
23
  export function whichPmRuns() {
2
24
  const userAgent = process.env.npm_config_user_agent;
3
25
  if (!userAgent) {
@@ -8,7 +30,7 @@ export function whichPmRuns() {
8
30
  return { name: "pnpm", version: "unknown" };
9
31
  }
10
32
 
11
- return undefined;
33
+ return detectFromLockfiles(process.cwd());
12
34
  }
13
35
 
14
36
  const pmSpec = userAgent.split(" ")[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pob/root",
3
- "version": "24.5.0",
3
+ "version": "24.7.0",
4
4
  "description": "root package",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/christophehurpeau/pob",
@@ -17,6 +17,7 @@
17
17
  "bin": {
18
18
  "oxfmt": "./bin/oxfmt.cjs",
19
19
  "pob-check-commit-msg": "./bin/pob-check-commit-msg.js",
20
+ "pob-claude-run-lint-staged": "./bin/pob-claude-run-lint-staged.js",
20
21
  "pob-root-lint-staged": "./bin/pob-root-lint-staged.js",
21
22
  "pob-root-postinstall": "./bin/pob-root-postinstall.js",
22
23
  "pob-version": "./bin/pob-version.js",
@@ -66,9 +67,10 @@
66
67
  "json5": "^2.2.3",
67
68
  "lint-staged": "17.0.8",
68
69
  "oxfmt": "0.54.0",
70
+ "picomatch": "^4.0.5",
69
71
  "semver": "^7.7.3",
70
- "@pob/version": "4.3.0",
71
- "pob-dependencies": "24.2.0"
72
+ "@pob/version": "4.3.2",
73
+ "pob-dependencies": "24.4.0"
72
74
  },
73
75
  "engines": {
74
76
  "node": ">=22.18.0"