@isentinel/hooks 1.2.0 → 1.2.3
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/dist/hooks/lint-stop.mjs +23 -3
- package/dist/hooks/type-check-stop.mjs +21 -4
- package/dist/scripts/lint.d.mts +8 -3
- package/dist/scripts/lint.mjs +145 -27
- package/package.json +6 -2
package/dist/hooks/lint-stop.mjs
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { findSourceRoot, getTransitiveDependents, isLintableFile, lint, readEditedFiles, readLintAttempts, readSettings, readStopAttempts, stopDecision, writeStopAttempts } from "../scripts/lint.mjs";
|
|
1
|
+
import { findSourceRoot, getTransitiveDependents, isLintableFile, lint, readEditedFiles, readLintAttempts, readSettings, readStopAttempts, restartDaemon, stopDecision, writeStopAttempts } from "../scripts/lint.mjs";
|
|
2
2
|
import { n as writeStdoutJson, t as readStdinJson } from "../io.mjs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
|
|
6
6
|
//#region hooks/lint-stop.ts
|
|
7
|
+
const debugLog = [];
|
|
7
8
|
const settings = readSettings();
|
|
9
|
+
function debug(message) {
|
|
10
|
+
if (settings.debug) debugLog.push(message);
|
|
11
|
+
}
|
|
8
12
|
if (!settings.lint) process.exit(0);
|
|
9
13
|
const SESSION_ID = (await readStdinJson()).session_id;
|
|
10
14
|
const editedFiles = readEditedFiles(SESSION_ID);
|
|
15
|
+
debug(`editedFiles=${JSON.stringify(editedFiles)}`);
|
|
11
16
|
if (editedFiles.length === 0) process.exit(0);
|
|
12
17
|
const dependents = /* @__PURE__ */ new Set();
|
|
13
18
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18,21 +23,36 @@ for (const file of editedFiles) {
|
|
|
18
23
|
for (const dependent of getTransitiveDependents(editedFiles, sourceRoot, settings.runner)) dependents.add(dependent);
|
|
19
24
|
}
|
|
20
25
|
const files = [...new Set([...editedFiles, ...dependents])].filter((file) => isLintableFile(file));
|
|
26
|
+
debug(`lintable files: ${JSON.stringify(files)}`);
|
|
21
27
|
if (files.length === 0) process.exit(0);
|
|
22
28
|
const errorFiles = [];
|
|
23
|
-
for (const file of files)
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
debug(`linting: ${file}`);
|
|
31
|
+
const lintResult = lint(file, ["--fix"], settings, { restart: false });
|
|
32
|
+
debug(`result: ${lintResult === void 0 ? "ok" : "errors"}`);
|
|
33
|
+
if (lintResult !== void 0) errorFiles.push(file);
|
|
34
|
+
}
|
|
35
|
+
debug(`errorFiles: ${JSON.stringify(errorFiles)}`);
|
|
36
|
+
debug("restartDaemon: start");
|
|
37
|
+
restartDaemon(settings.runner);
|
|
38
|
+
debug("restartDaemon: end");
|
|
24
39
|
const result = stopDecision({
|
|
25
40
|
errorFiles,
|
|
26
41
|
lintAttempts: readLintAttempts(),
|
|
27
42
|
maxLintAttempts: settings.maxLintAttempts,
|
|
28
43
|
stopAttempts: readStopAttempts()
|
|
29
44
|
});
|
|
30
|
-
|
|
45
|
+
debug(`stopDecision: ${JSON.stringify(result)}`);
|
|
46
|
+
if (result === void 0) {
|
|
47
|
+
if (debugLog.length > 0) writeStdoutJson({ reason: `[lint-stop debug]\n${debugLog.join("\n")}` });
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
31
50
|
if (result.resetStopAttempts) {
|
|
32
51
|
writeStopAttempts(0);
|
|
33
52
|
process.exit(0);
|
|
34
53
|
}
|
|
35
54
|
writeStopAttempts(readStopAttempts() + 1);
|
|
55
|
+
if (debugLog.length > 0) result.reason = `${result.reason ?? ""}\n\n[lint-stop debug]\n${debugLog.join("\n")}`;
|
|
36
56
|
writeStdoutJson(result);
|
|
37
57
|
|
|
38
58
|
//#endregion
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { findSourceRoot, getTransitiveDependents, readEditedFiles, readLintAttempts, readSettings } from "../scripts/lint.mjs";
|
|
2
2
|
import { isTypeCheckable, readTypecheckStopAttempts, resolveTsconfig, runTypeCheck, typecheckStopDecision, writeTypecheckStopAttempts } from "../scripts/type-check.mjs";
|
|
3
3
|
import { n as writeStdoutJson, t as readStdinJson } from "../io.mjs";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
4
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
|
|
7
7
|
//#region hooks/type-check-stop.ts
|
|
8
|
+
const debugLog = [];
|
|
8
9
|
const settings = readSettings();
|
|
10
|
+
function debug(message) {
|
|
11
|
+
if (settings.debug) debugLog.push(message);
|
|
12
|
+
}
|
|
9
13
|
if (!settings.typecheck) process.exit(0);
|
|
10
14
|
const SESSION_ID = (await readStdinJson()).session_id;
|
|
11
15
|
const PROJECT_ROOT = process.env["CLAUDE_PROJECT_DIR"] ?? process.cwd();
|
|
12
16
|
const editedFiles = readEditedFiles(SESSION_ID);
|
|
17
|
+
debug(`editedFiles=${JSON.stringify(editedFiles)}`);
|
|
13
18
|
if (editedFiles.length === 0) process.exit(0);
|
|
14
19
|
const allFiles = new Set(editedFiles);
|
|
15
20
|
const seenRoots = /* @__PURE__ */ new Set();
|
|
@@ -20,28 +25,40 @@ for (const file of editedFiles) {
|
|
|
20
25
|
for (const dependent of getTransitiveDependents(editedFiles, sourceRoot, settings.runner)) allFiles.add(dependent);
|
|
21
26
|
}
|
|
22
27
|
const files = [...allFiles].filter((file) => isTypeCheckable(file));
|
|
28
|
+
debug(`type-checkable files: ${JSON.stringify(files)}`);
|
|
23
29
|
if (files.length === 0) process.exit(0);
|
|
24
30
|
const errorFiles = [];
|
|
25
31
|
for (const file of files) {
|
|
26
|
-
const
|
|
32
|
+
const absolutePath = isAbsolute(file) ? file : join(PROJECT_ROOT, file);
|
|
33
|
+
debug(`file=${file} isAbsolute=${String(isAbsolute(file))} absolutePath=${absolutePath}`);
|
|
34
|
+
const tsconfig = resolveTsconfig(absolutePath, PROJECT_ROOT);
|
|
35
|
+
debug(`tsconfig=${String(tsconfig)}`);
|
|
27
36
|
if (tsconfig === void 0) continue;
|
|
28
37
|
const output = runTypeCheck(tsconfig, settings.runner, settings.typecheckArgs);
|
|
29
38
|
if (output !== void 0) {
|
|
30
|
-
|
|
39
|
+
const hasErrors = /error TS/i.test(output);
|
|
40
|
+
debug(`typecheck ${file}: ${hasErrors ? "errors" : "ok"}`);
|
|
41
|
+
if (hasErrors) errorFiles.push(file);
|
|
31
42
|
}
|
|
32
43
|
}
|
|
44
|
+
debug(`errorFiles: ${JSON.stringify(errorFiles)}`);
|
|
33
45
|
const result = typecheckStopDecision({
|
|
34
46
|
errorFiles,
|
|
35
47
|
lintAttempts: readLintAttempts(),
|
|
36
48
|
maxLintAttempts: settings.maxLintAttempts,
|
|
37
49
|
stopAttempts: readTypecheckStopAttempts()
|
|
38
50
|
});
|
|
39
|
-
|
|
51
|
+
debug(`stopDecision: ${JSON.stringify(result)}`);
|
|
52
|
+
if (result === void 0) {
|
|
53
|
+
if (debugLog.length > 0) writeStdoutJson({ reason: `[type-check-stop debug]\n${debugLog.join("\n")}` });
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
40
56
|
if (result.resetStopAttempts) {
|
|
41
57
|
writeTypecheckStopAttempts(0);
|
|
42
58
|
process.exit(0);
|
|
43
59
|
}
|
|
44
60
|
writeTypecheckStopAttempts(readTypecheckStopAttempts() + 1);
|
|
61
|
+
if (debugLog.length > 0) result.reason = `${result.reason ?? ""}\n\n[type-check-stop debug]\n${debugLog.join("\n")}`;
|
|
45
62
|
writeStdoutJson(result);
|
|
46
63
|
|
|
47
64
|
//#endregion
|
package/dist/scripts/lint.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { t as PostToolUseHookOutput } from "../events.mjs";
|
|
|
3
3
|
//#region scripts/lint.d.ts
|
|
4
4
|
interface LintSettings {
|
|
5
5
|
cacheBust: Array<string>;
|
|
6
|
+
debug: boolean;
|
|
6
7
|
eslint: boolean;
|
|
7
8
|
lint: boolean;
|
|
8
9
|
maxLintAttempts: number;
|
|
@@ -49,10 +50,14 @@ declare function clearCache(): void;
|
|
|
49
50
|
declare function invalidateCacheEntries(filePaths: Array<string>): void;
|
|
50
51
|
declare function runOxlint(filePath: string, extraFlags?: Array<string>, runner?: string): string | undefined;
|
|
51
52
|
declare function runEslint(filePath: string, extraFlags?: Array<string>, runner?: string): string | undefined;
|
|
52
|
-
declare function restartDaemon(runner?: string): void;
|
|
53
|
+
declare function restartDaemon(runner?: string, warmupFile?: string): void;
|
|
53
54
|
declare function formatErrors(output: string): Array<string>;
|
|
54
|
-
declare function buildHookOutput(filePath: string, errors: Array<string
|
|
55
|
-
declare function lint(filePath: string, extraFlags?: Array<string>, settings?: LintSettings
|
|
55
|
+
declare function buildHookOutput(filePath: string, errors: Array<string>, debugInfo?: string): PostToolUseHookOutput;
|
|
56
|
+
declare function lint(filePath: string, extraFlags?: Array<string>, settings?: LintSettings, {
|
|
57
|
+
restart
|
|
58
|
+
}?: {
|
|
59
|
+
restart?: boolean | undefined;
|
|
60
|
+
}): PostToolUseHookOutput | undefined;
|
|
56
61
|
declare function main(targets: Array<string>, settings?: LintSettings): void;
|
|
57
62
|
//#endregion
|
|
58
63
|
export { DEFAULT_CACHE_BUST, DependencyGraph, LintSettings, StopDecisionResult, buildHookOutput, clearCache, clearEditedFiles, clearLintAttempts, clearStopAttempts, findEntryPoints, findSourceRoot, formatErrors, getChangedFiles, getDependencyGraph, getTransitiveDependents, invalidateCacheEntries, invertGraph, isLintableFile, isProtectedFile, lint, main, readEditedFiles, readLintAttempts, readSettings, readStopAttempts, resolveBustFiles, restartDaemon, runEslint, runOxlint, shouldBustCache, stopDecision, writeEditedFile, writeLintAttempts, writeStopAttempts };
|
package/dist/scripts/lint.mjs
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
import { createFromFile } from "file-entry-cache";
|
|
2
|
-
import { execSync, spawn } from "node:child_process";
|
|
2
|
+
import { execFileSync, execSync, spawn, spawnSync } from "node:child_process";
|
|
3
3
|
import { existsSync, globSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
4
5
|
import { dirname, join, relative, resolve } from "node:path";
|
|
5
6
|
import process from "node:process";
|
|
6
7
|
|
|
7
8
|
//#region scripts/lint.ts
|
|
9
|
+
function spawnBackground(script, extraEnvironment = {}) {
|
|
10
|
+
const scriptFile = join(tmpdir(), `.eslint_bg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}.cjs`);
|
|
11
|
+
writeFileSync(scriptFile, script);
|
|
12
|
+
const environment = {
|
|
13
|
+
...process.env,
|
|
14
|
+
...extraEnvironment
|
|
15
|
+
};
|
|
16
|
+
if (process.platform === "win32") spawnSync("powershell.exe", [
|
|
17
|
+
"-NoProfile",
|
|
18
|
+
"-Command",
|
|
19
|
+
`Start-Process -FilePath 'node' -ArgumentList '${scriptFile}' -WindowStyle Hidden`
|
|
20
|
+
], {
|
|
21
|
+
env: environment,
|
|
22
|
+
stdio: "ignore",
|
|
23
|
+
windowsHide: true
|
|
24
|
+
});
|
|
25
|
+
else spawn("node", [scriptFile], {
|
|
26
|
+
detached: true,
|
|
27
|
+
env: environment,
|
|
28
|
+
stdio: "ignore"
|
|
29
|
+
}).unref();
|
|
30
|
+
}
|
|
8
31
|
const PROTECTED_PATTERNS = [
|
|
9
32
|
"eslint.config.",
|
|
10
33
|
"oxlint.config.",
|
|
@@ -17,6 +40,9 @@ function isProtectedFile(filename) {
|
|
|
17
40
|
const LINT_STATE_PATH = ".claude/state/lint-attempts.json";
|
|
18
41
|
const STOP_STATE_PATH = ".claude/state/stop-attempts.json";
|
|
19
42
|
const EDITED_FILES_PATH = ".claude/state/edited-files.json";
|
|
43
|
+
const RESTART_DAEMON_LOG = ".claude/state/restartDaemon.log";
|
|
44
|
+
const IS_RESTART_DAEMON_DEBUG = false;
|
|
45
|
+
const CLAUDE_PID_PATH = ".claude/state/claude-pid";
|
|
20
46
|
function readEditedFiles(sessionId) {
|
|
21
47
|
if (!existsSync(EDITED_FILES_PATH)) return [];
|
|
22
48
|
try {
|
|
@@ -74,6 +100,43 @@ function getTransitiveDependents(files, sourceRoot, runner = DEFAULT_SETTINGS.ru
|
|
|
74
100
|
const originals = new Set(files.map((file) => relative(sourceRoot, resolve(file)).replaceAll("\\", "/")));
|
|
75
101
|
return [...visited].filter((file) => !originals.has(file)).map((file) => join(sourceRoot, file));
|
|
76
102
|
}
|
|
103
|
+
function getClaudePid() {
|
|
104
|
+
const ssePort = process.env["CLAUDE_CODE_SSE_PORT"] ?? "";
|
|
105
|
+
const cacheFile = ssePort.length > 0 ? `${CLAUDE_PID_PATH}-${ssePort}` : CLAUDE_PID_PATH;
|
|
106
|
+
if (existsSync(cacheFile)) try {
|
|
107
|
+
const cached = readFileSync(cacheFile, "utf-8").trim();
|
|
108
|
+
const pid = Number(cached);
|
|
109
|
+
process.kill(pid, 0);
|
|
110
|
+
return cached;
|
|
111
|
+
} catch {}
|
|
112
|
+
if (process.platform === "win32") try {
|
|
113
|
+
const result = execFileSync("powershell.exe", [
|
|
114
|
+
"-NoProfile",
|
|
115
|
+
"-Command",
|
|
116
|
+
`
|
|
117
|
+
$currentPid = ${process.ppid}
|
|
118
|
+
while ($currentPid -and $currentPid -ne 0) {
|
|
119
|
+
$p = Get-CimInstance Win32_Process -Filter "ProcessId=$currentPid" -Property Name,ParentProcessId -ErrorAction SilentlyContinue
|
|
120
|
+
if (-not $p) { break }
|
|
121
|
+
if ($p.Name -eq 'claude.exe') { Write-Output $currentPid; exit }
|
|
122
|
+
$currentPid = $p.ParentProcessId
|
|
123
|
+
}`
|
|
124
|
+
], {
|
|
125
|
+
encoding: "utf-8",
|
|
126
|
+
stdio: [
|
|
127
|
+
"pipe",
|
|
128
|
+
"pipe",
|
|
129
|
+
"pipe"
|
|
130
|
+
],
|
|
131
|
+
timeout: 5e3
|
|
132
|
+
}).trim();
|
|
133
|
+
if (result.length > 0) {
|
|
134
|
+
mkdirSync(dirname(cacheFile), { recursive: true });
|
|
135
|
+
writeFileSync(cacheFile, result);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
} catch {}
|
|
139
|
+
}
|
|
77
140
|
const ESLINT_CACHE_PATH = ".eslintcache";
|
|
78
141
|
const DEFAULT_EXTENSIONS = [
|
|
79
142
|
".ts",
|
|
@@ -96,6 +159,7 @@ const DEFAULT_MAX_LINT_ATTEMPTS = 1;
|
|
|
96
159
|
const DEFAULT_MAX_STOP_ATTEMPTS = 1;
|
|
97
160
|
const DEFAULT_SETTINGS = {
|
|
98
161
|
cacheBust: [...DEFAULT_CACHE_BUST],
|
|
162
|
+
debug: false,
|
|
99
163
|
eslint: true,
|
|
100
164
|
lint: true,
|
|
101
165
|
maxLintAttempts: DEFAULT_MAX_LINT_ATTEMPTS,
|
|
@@ -113,6 +177,7 @@ function readSettings() {
|
|
|
113
177
|
const maxLintAttempts = maxAttemptsRaw !== void 0 ? Number(maxAttemptsRaw) : DEFAULT_MAX_LINT_ATTEMPTS;
|
|
114
178
|
return {
|
|
115
179
|
cacheBust: [...DEFAULT_CACHE_BUST, ...userPatterns],
|
|
180
|
+
debug: fields.get("debug") === "true",
|
|
116
181
|
eslint: fields.get("eslint") !== "false",
|
|
117
182
|
lint: fields.get("lint") !== "false",
|
|
118
183
|
maxLintAttempts,
|
|
@@ -258,9 +323,11 @@ function runOxlint(filePath, extraFlags = [], runner = DEFAULT_SETTINGS.runner)
|
|
|
258
323
|
function runEslint(filePath, extraFlags = [], runner = DEFAULT_SETTINGS.runner) {
|
|
259
324
|
const flags = ["--cache", ...extraFlags].join(" ");
|
|
260
325
|
try {
|
|
326
|
+
const claudePid = getClaudePid();
|
|
261
327
|
execSync(`${runner} eslint_d ${flags} "${filePath}"`, {
|
|
262
328
|
env: {
|
|
263
329
|
...process.env,
|
|
330
|
+
...claudePid !== void 0 && { ESLINT_D_PPID: claudePid },
|
|
264
331
|
ESLINT_IN_EDITOR: "true"
|
|
265
332
|
},
|
|
266
333
|
stdio: "pipe"
|
|
@@ -274,45 +341,91 @@ function runEslint(filePath, extraFlags = [], runner = DEFAULT_SETTINGS.runner)
|
|
|
274
341
|
return stdout || stderr || message;
|
|
275
342
|
}
|
|
276
343
|
}
|
|
277
|
-
function restartDaemon(runner = DEFAULT_SETTINGS.runner) {
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
344
|
+
function restartDaemon(runner = DEFAULT_SETTINGS.runner, warmupFile) {
|
|
345
|
+
const markerFile = void 0;
|
|
346
|
+
try {
|
|
347
|
+
const restartAndWarmupScript = `
|
|
348
|
+
const { execSync } = require("child_process");
|
|
349
|
+
const fs = require("fs");
|
|
350
|
+
|
|
351
|
+
const runner = ${JSON.stringify(runner)};
|
|
352
|
+
const warmupFile = ${JSON.stringify(warmupFile)};
|
|
353
|
+
const debug = ${IS_RESTART_DAEMON_DEBUG};
|
|
354
|
+
const logFile = ${JSON.stringify(RESTART_DAEMON_LOG)};
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
// Run restart
|
|
358
|
+
if (debug) {
|
|
359
|
+
fs.appendFileSync(logFile, \`restart: start \${new Date().toISOString()}\\n\`);
|
|
360
|
+
}
|
|
361
|
+
execSync(\`\${runner} eslint_d restart\`, {
|
|
362
|
+
env: { ...process.env, ESLINT_D_PPID: process.env.ESLINT_D_PPID, ESLINT_IN_EDITOR: "true" },
|
|
363
|
+
stdio: "pipe",
|
|
364
|
+
});
|
|
365
|
+
if (debug) {
|
|
366
|
+
fs.appendFileSync(logFile, \`restart: end \${new Date().toISOString()}\\n\`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Run warmup lint if file provided
|
|
370
|
+
if (warmupFile) {
|
|
371
|
+
if (debug) {
|
|
372
|
+
fs.appendFileSync(logFile, \`warmup: start \${warmupFile} \${new Date().toISOString()}\\n\`);
|
|
373
|
+
}
|
|
374
|
+
execSync(\`\${runner} eslint_d "\${warmupFile}"\`, {
|
|
375
|
+
env: { ...process.env, ESLINT_D_PPID: process.env.ESLINT_D_PPID, ESLINT_IN_EDITOR: "true" },
|
|
376
|
+
stdio: "pipe",
|
|
377
|
+
});
|
|
378
|
+
if (debug) {
|
|
379
|
+
fs.appendFileSync(logFile, \`warmup: end \${new Date().toISOString()}\\n\`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} catch (err) {
|
|
383
|
+
if (debug) {
|
|
384
|
+
fs.appendFileSync(logFile, \`error: \${err.message} \${new Date().toISOString()}\\n\`);
|
|
385
|
+
}
|
|
386
|
+
} finally {
|
|
387
|
+
try { fs.unlinkSync(process.argv[1]); } catch {}
|
|
388
|
+
}
|
|
389
|
+
`;
|
|
390
|
+
const claudePid = getClaudePid();
|
|
391
|
+
spawnBackground(restartAndWarmupScript, {
|
|
392
|
+
...claudePid !== void 0 && { ESLINT_D_PPID: claudePid },
|
|
287
393
|
ESLINT_IN_EDITOR: "true"
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
394
|
+
});
|
|
395
|
+
if (markerFile !== void 0) spawnBackground(`
|
|
396
|
+
const fs = require("fs");
|
|
397
|
+
const logFile = ${JSON.stringify(RESTART_DAEMON_LOG)};
|
|
398
|
+
|
|
399
|
+
setTimeout(() => {
|
|
400
|
+
try {
|
|
401
|
+
fs.appendFileSync(logFile, \`daemon exit: \${new Date().toISOString()}\\n\`);
|
|
402
|
+
} catch {}
|
|
403
|
+
try { fs.unlinkSync(process.argv[1]); } catch {}
|
|
404
|
+
process.exit(0);
|
|
405
|
+
}, 10000);
|
|
406
|
+
`);
|
|
407
|
+
} catch (err) {
|
|
408
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
409
|
+
process.stderr.write(`[eslint_d restart] ${message}\n`);
|
|
410
|
+
}
|
|
298
411
|
}
|
|
299
412
|
function formatErrors(output) {
|
|
300
413
|
return output.split("\n").filter((line) => /error/i.test(line)).slice(0, MAX_ERRORS);
|
|
301
414
|
}
|
|
302
|
-
function buildHookOutput(filePath, errors) {
|
|
415
|
+
function buildHookOutput(filePath, errors, debugInfo = "") {
|
|
303
416
|
const errorText = errors.join("\n");
|
|
304
417
|
const isTruncated = errors.length >= MAX_ERRORS;
|
|
305
|
-
const userMessage = `⚠️ Lint errors in ${filePath}:\n${errorText}${isTruncated ? "\n..." : ""}`;
|
|
418
|
+
const userMessage = `⚠️ Lint errors in ${filePath}:\n${errorText}${isTruncated ? "\n..." : ""}${debugInfo}`;
|
|
306
419
|
return {
|
|
307
420
|
decision: void 0,
|
|
308
421
|
hookSpecificOutput: {
|
|
309
|
-
additionalContext: `⚠️ Lint errors in ${filePath}:\n${errorText}${isTruncated ? "\n(run lint to view more)" : ""}`,
|
|
422
|
+
additionalContext: `⚠️ Lint errors in ${filePath}:\n${errorText}${isTruncated ? "\n(run lint to view more)" : ""}${debugInfo}`,
|
|
310
423
|
hookEventName: "PostToolUse"
|
|
311
424
|
},
|
|
312
425
|
systemMessage: userMessage
|
|
313
426
|
};
|
|
314
427
|
}
|
|
315
|
-
function lint(filePath, extraFlags = [], settings = DEFAULT_SETTINGS) {
|
|
428
|
+
function lint(filePath, extraFlags = [], settings = DEFAULT_SETTINGS, { restart = true } = {}) {
|
|
316
429
|
if (shouldBustCache(settings.cacheBust)) clearCache();
|
|
317
430
|
else invalidateCacheEntries(findImporters(filePath, settings.runner));
|
|
318
431
|
const outputs = [];
|
|
@@ -324,10 +437,15 @@ function lint(filePath, extraFlags = [], settings = DEFAULT_SETTINGS) {
|
|
|
324
437
|
const output = runEslint(filePath, extraFlags, settings.runner);
|
|
325
438
|
if (output !== void 0) outputs.push(output);
|
|
326
439
|
}
|
|
327
|
-
|
|
440
|
+
let debugInfo = "";
|
|
441
|
+
if (settings.eslint && restart) {
|
|
442
|
+
const startTime = Date.now();
|
|
443
|
+
restartDaemon(settings.runner, filePath);
|
|
444
|
+
Date.now() - startTime;
|
|
445
|
+
}
|
|
328
446
|
if (outputs.length > 0) {
|
|
329
447
|
const errors = formatErrors(outputs.join("\n"));
|
|
330
|
-
if (errors.length > 0) return buildHookOutput(filePath, errors);
|
|
448
|
+
if (errors.length > 0) return buildHookOutput(filePath, errors, debugInfo);
|
|
331
449
|
}
|
|
332
450
|
}
|
|
333
451
|
function main(targets, settings = DEFAULT_SETTINGS) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isentinel/hooks",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Claude Code hooks for linting and type-checking TypeScript projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -42,12 +42,13 @@
|
|
|
42
42
|
"*.{ts,mts}": "eslint --fix"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"eslint_d": "^
|
|
45
|
+
"eslint_d": "^15.0.0",
|
|
46
46
|
"file-entry-cache": "^11.1.2",
|
|
47
47
|
"get-tsconfig": "^4.13.6",
|
|
48
48
|
"madge": "^8.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
+
"@antfu/ni": "^29.0.0",
|
|
51
52
|
"@clack/prompts": "^0.11.0",
|
|
52
53
|
"@constellos/claude-code-kit": "^0.4.0",
|
|
53
54
|
"@isentinel/eslint-config": "5.0.0-beta.8",
|
|
@@ -81,6 +82,9 @@
|
|
|
81
82
|
"engines": {
|
|
82
83
|
"node": ">=24.11.0"
|
|
83
84
|
},
|
|
85
|
+
"publishConfig": {
|
|
86
|
+
"provenance": true
|
|
87
|
+
},
|
|
84
88
|
"scripts": {
|
|
85
89
|
"build": "tsdown",
|
|
86
90
|
"lint": "eslint --cache",
|