@moneko/core 4.12.3 → 4.12.4
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/hooks/ai-eslint.sh +44 -80
- package/package.json +1 -1
package/hooks/ai-eslint.sh
CHANGED
|
@@ -1,102 +1,66 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# Shared AI Agent ESLint fix Stop hook.
|
|
4
|
-
# Used by both Claude Code (project .claude/settings.json) and Codex
|
|
5
|
-
# (project .codex/config.toml). Runs the project's own `npx mo eslint <file> --fix`
|
|
6
|
-
# over the files the Agent just created/modified/untracked, then blocks the
|
|
7
|
-
# Agent Stop until the remaining ESLint errors are fixed.
|
|
8
|
-
#
|
|
9
|
-
# Exit codes:
|
|
10
|
-
# 0 no target files, or all errors were auto-fixed
|
|
11
|
-
# 2 at least one file still has ESLint errors after --fix (blocks Stop)
|
|
12
2
|
set -u
|
|
13
3
|
|
|
14
|
-
# ---------------------------------------------------------------------------
|
|
15
|
-
# Recursion guard. Both Claude Code and Codex pass a JSON payload on stdin for
|
|
16
|
-
# Stop hooks. When a Stop hook blocks (exit 2), the platform starts a follow-up
|
|
17
|
-
# turn and sets `stop_hook_active` to true in that payload. We must not block
|
|
18
|
-
# again on the follow-up turn, otherwise the Agent loops forever.
|
|
19
|
-
# ---------------------------------------------------------------------------
|
|
20
4
|
if [ ! -t 0 ]; then
|
|
21
|
-
hook_input
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
5
|
+
hook_input=$(cat)
|
|
6
|
+
if printf '%s' "$hook_input" | grep -Eq '"stop_hook_active"[[:space:]]*:[[:space:]]*true'; then
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
25
9
|
fi
|
|
26
10
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
cd "$root" || exit 0
|
|
30
|
-
|
|
31
|
-
if ! command -v npx >/dev/null 2>&1; then
|
|
32
|
-
printf '%s\n' 'AI ESLint stop hook: npx is unavailable; install the existing project dependencies before retrying. No dependency was installed.' >&2
|
|
11
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || {
|
|
12
|
+
printf 'AI ESLint hook: unable to locate the Git repository root.\n' >&2
|
|
33
13
|
exit 2
|
|
34
|
-
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
cd "$repo_root" || exit 2
|
|
35
17
|
|
|
36
|
-
if !
|
|
37
|
-
printf '
|
|
18
|
+
if ! command -v npx >/dev/null 2>&1; then
|
|
19
|
+
printf 'AI ESLint hook: npx is unavailable; install no dependency automatically and fix the environment before continuing.\n' >&2
|
|
38
20
|
exit 2
|
|
39
21
|
fi
|
|
40
22
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# spaces / CJK / other characters intact and correctly consumes the second
|
|
44
|
-
# NUL-terminated path of rename/copy records.
|
|
45
|
-
# ---------------------------------------------------------------------------
|
|
46
|
-
targets=()
|
|
47
|
-
while IFS= read -r -d '' entry; do
|
|
48
|
-
x="${entry:0:1}" # index status: ' ', M, A, D, R, C, U, ?
|
|
49
|
-
y="${entry:1:1}" # worktree status: ' ', M, D, U, ?
|
|
50
|
-
path="${entry:3}" # skip the "XY " prefix and preserve the full path
|
|
23
|
+
tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/ai-eslint-fix.XXXXXX") || exit 2
|
|
24
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
51
25
|
|
|
52
|
-
|
|
53
|
-
# Consume it so the next status record is not misaligned.
|
|
54
|
-
if [ "$x" = R ] || [ "$x" = C ]; then
|
|
55
|
-
IFS= read -r -d '' _src || true
|
|
56
|
-
fi
|
|
26
|
+
had_errors=0
|
|
57
27
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
fi
|
|
28
|
+
while IFS= read -r -d '' record; do
|
|
29
|
+
xy=${record:0:2}
|
|
30
|
+
path=${record:3}
|
|
62
31
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
32
|
+
case "$xy" in
|
|
33
|
+
*R*|*C*)
|
|
34
|
+
# Rename/copy records contain a second NUL-delimited path. Always consume it.
|
|
35
|
+
IFS= read -r -d '' ignored_path || true
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
67
38
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
39
|
+
case "$xy" in
|
|
40
|
+
\?\?) should_lint=1 ;;
|
|
41
|
+
*)
|
|
42
|
+
case "$xy" in
|
|
43
|
+
*A*|*M*) should_lint=1 ;;
|
|
44
|
+
*) should_lint=0 ;;
|
|
45
|
+
esac
|
|
46
|
+
;;
|
|
47
|
+
esac
|
|
73
48
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# the remaining files; collect all output and report it once at the end.
|
|
77
|
-
# ---------------------------------------------------------------------------
|
|
78
|
-
failed=0
|
|
79
|
-
collected=""
|
|
80
|
-
for file in "${targets[@]:-}"; do
|
|
81
|
-
# Skip paths that no longer exist on disk.
|
|
82
|
-
[ -e "$file" ] || continue
|
|
49
|
+
[ "$should_lint" -eq 1 ] || continue
|
|
50
|
+
[ -e "$path" ] || [ -L "$path" ] || continue
|
|
83
51
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
failed=1
|
|
88
|
-
collected+="${out}"$'\n'
|
|
52
|
+
output_file="$tmp_dir/output-${RANDOM}-${RANDOM}"
|
|
53
|
+
if ! npx mo eslint "$path" --fix >"$output_file" 2>&1; then
|
|
54
|
+
had_errors=1
|
|
89
55
|
fi
|
|
90
|
-
|
|
56
|
+
if [ -s "$output_file" ]; then
|
|
57
|
+
cat "$output_file" >&2
|
|
58
|
+
fi
|
|
59
|
+
done < <(git status --porcelain=v1 -z --untracked-files=all)
|
|
91
60
|
|
|
92
|
-
if [ "$
|
|
93
|
-
|
|
94
|
-
[ -n "$collected" ] && printf '%s\n' "$collected"
|
|
95
|
-
printf '%s\n' '============================================================'
|
|
96
|
-
printf '%s\n' 'AI ESLint stop hook: 以上文件在 --fix 后仍存在 ESLint error。'
|
|
97
|
-
printf '%s\n' '请继续修复剩余的 ESLint 问题后再结束当前任务。'
|
|
98
|
-
} >&2
|
|
61
|
+
if [ "$had_errors" -ne 0 ]; then
|
|
62
|
+
printf '\nAI ESLint hook: 请 Agent 继续修复剩余 ESLint 问题。\n' >&2
|
|
99
63
|
exit 2
|
|
100
64
|
fi
|
|
101
65
|
|
|
102
|
-
exit 0
|
|
66
|
+
exit 0
|