@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.
Files changed (2) hide show
  1. package/hooks/ai-eslint.sh +44 -80
  2. package/package.json +1 -1
@@ -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="$(cat 2>/dev/null || true)"
22
- case "$hook_input" in
23
- *'"stop_hook_active"':*'true'*) exit 0 ;;
24
- esac
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
- # Locate the repository root so this works from any subdirectory.
28
- root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
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
- fi
14
+ }
15
+
16
+ cd "$repo_root" || exit 2
35
17
 
36
- if ! npx --no-install mo --version >/dev/null 2>&1; then
37
- printf '%s\n' 'AI ESLint stop hook: project command "mo" is unavailable; install the existing project dependencies before retrying. No dependency was installed.' >&2
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
- # Collect target files from NUL-separated porcelain v1 output. This keeps
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
- # Rename (R) / copy (C) records carry a second NUL-terminated source path.
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
- # Deleted files are never processed.
59
- if [ "$x" = D ] || [ "$y" = D ]; then
60
- continue
61
- fi
28
+ while IFS= read -r -d '' record; do
29
+ xy=${record:0:2}
30
+ path=${record:3}
62
31
 
63
- # Pure rename/copy (no added or modified content) is not processed.
64
- if { [ "$x" = R ] || [ "$x" = C ]; } && [ "$y" != M ]; then
65
- continue
66
- fi
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
- # Process: modified (staged or working-tree), added, or untracked.
69
- if [ "$x" = M ] || [ "$y" = M ] || [ "$x" = A ] || { [ "$x" = '?' ] && [ "$y" = '?' ]; }; then
70
- targets+=("$path")
71
- fi
72
- done < <(git status --porcelain=v1 -z --untracked-files=all)
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
- # Run the project's own linter one file at a time. A failed file must not stop
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
- out="$(npx mo eslint "$file" --fix 2>&1)"
85
- rc=$?
86
- if [ "$rc" -ne 0 ]; then
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
- done
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 [ "$failed" -ne 0 ]; then
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneko/core",
3
- "version": "4.12.3",
3
+ "version": "4.12.4",
4
4
  "description": "@moneko/core",
5
5
  "main": "lib/index.mjs",
6
6
  "type": "module",