From aa624ea0fe0d6fc85ac105deeecfcf6cf002937f Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sat, 29 Nov 2025 18:25:21 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 12 +++++++ README.md | 3 ++ hooks/eslint-check.sh | 68 ++++++++++++++++++++++++++++++++++++++ hooks/hooks.json | 15 +++++++++ plugin.lock.json | 49 +++++++++++++++++++++++++++ 5 files changed, 147 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100755 hooks/eslint-check.sh create mode 100644 hooks/hooks.json create mode 100644 plugin.lock.json diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..3675722 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "eslint-plugin", + "description": "Automatically runs ESLint checks on JavaScript/TypeScript files after edits", + "version": "1.0.2", + "author": { + "name": "Eli Oshinsky", + "email": "elimelech.oshinsky@gmail.com" + }, + "hooks": [ + "./hooks" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b74feab --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# eslint-plugin + +Automatically runs ESLint checks on JavaScript/TypeScript files after edits diff --git a/hooks/eslint-check.sh b/hooks/eslint-check.sh new file mode 100755 index 0000000..6dd1c74 --- /dev/null +++ b/hooks/eslint-check.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# ESLint hook for Claude Code (Plugin Mode) +# Checks files with ESLint after edits and outputs JSON format + +# Source utilities +source "$(dirname "$0")/../../shared/hooks/utils.sh" + +# Helper function to output JSON and exit +output_json() { + echo "$1" + exit 0 +} + +# Read input from stdin +INPUT=$(cat) + +# Parse input and extract fields +if ! parse_hook_input "$INPUT"; then + output_json '{}' +fi + +# Check if project has local eslint hook override +if has_project_hook_override "$PROJECT_DIR" "eslint"; then + output_json '{}' +fi + +# Check if project has eslint configured +if ! has_eslint_config "$PROJECT_DIR"; then + output_json '{}' +fi + +# Get the appropriate package runner +PKG_RUNNER=$(get_package_runner) + +# Run ESLint on the file and capture output +ESLINT_OUTPUT=$(cd "$PROJECT_DIR" && $PKG_RUNNER eslint "$FILE_PATH" 2>&1) +ESLINT_EXIT_CODE=$? + +# Filter out "File ignored" errors from ESLint output +if echo "$ESLINT_OUTPUT" | grep -q "File ignored"; then + output_json '{}' +fi + +# Filter out "couldn't find an eslint" errors (no config file found) +if echo "$ESLINT_OUTPUT" | grep -iq "couldn't find an eslint"; then + output_json '{}' +fi + +if [[ $ESLINT_EXIT_CODE -eq 0 ]]; then + # Success - no errors found + output_json '{}' +elif [[ $ESLINT_EXIT_CODE -eq 1 ]]; then + # Rule violations - block the agent as they are actionable + # Escape the output for JSON + ESCAPED_OUTPUT=$(echo "$ESLINT_OUTPUT" | jq -Rs .) + output_json "{\"decision\": \"block\", \"reason\": $ESCAPED_OUTPUT}" +elif [[ $ESLINT_EXIT_CODE -eq 2 ]]; then + # Technical issues (config errors, missing deps, etc.) - don't block but provide feedback + ERROR_MSG="ESLint encountered a technical issue with $FILE_PATH (exit code 2): $ESLINT_OUTPUT" + ESCAPED_MSG=$(echo "$ERROR_MSG" | jq -Rs .) + output_json "{\"hookSpecificOutput\": {\"hookEventName\": \"PostToolUse\", \"additionalContext\": $ESCAPED_MSG}}" +else + # Unexpected exit code - don't block but provide feedback + ERROR_MSG="ESLint returned unexpected exit code $ESLINT_EXIT_CODE for $FILE_PATH: $ESLINT_OUTPUT" + ESCAPED_MSG=$(echo "$ERROR_MSG" | jq -Rs .) + output_json "{\"hookSpecificOutput\": {\"hookEventName\": \"PostToolUse\", \"additionalContext\": $ESCAPED_MSG}}" +fi diff --git a/hooks/hooks.json b/hooks/hooks.json new file mode 100644 index 0000000..efa8c21 --- /dev/null +++ b/hooks/hooks.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/eslint-check.sh" + } + ] + } + ] + } +} diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..702c292 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,49 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:eli0shin/cli-lsp-client:claude-plugins/eslint-plugin", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "6683c6af5f9cf12655a6ad2fb3e6de13c59d5ac9", + "treeHash": "0e222327d3052eda30f4738ca9eee03faa69abd3611475d9deb9b6998889e789", + "generatedAt": "2025-11-28T10:16:45.790251Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "eslint-plugin", + "description": "Automatically runs ESLint checks on JavaScript/TypeScript files after edits", + "version": "1.0.2" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "11c58a30f0ab3be3c456d2f705884c2de6427affbe6a29e6cf0e4ab87f653463" + }, + { + "path": "hooks/hooks.json", + "sha256": "669874d653b2808e6fed0d5b9003a6569b0b5c763ad90b2d6d50516fa4efe684" + }, + { + "path": "hooks/eslint-check.sh", + "sha256": "b62e746f4bcd512571a34f2e6a5c7cb9074248d927b7c9179c16efc53dee7b18" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "c7c6f46f8fa18982e969194075e32f4917c811f2e96ac72274912bac33fb2635" + } + ], + "dirSha256": "0e222327d3052eda30f4738ca9eee03faa69abd3611475d9deb9b6998889e789" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file