@codyswann/lisa 2.121.0 → 2.121.1
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/agy/mcp-installer.d.ts +18 -0
- package/dist/agy/mcp-installer.d.ts.map +1 -1
- package/dist/agy/mcp-installer.js +21 -0
- package/dist/agy/mcp-installer.js.map +1 -1
- package/dist/agy/plugin-installer.d.ts.map +1 -1
- package/dist/agy/plugin-installer.js +6 -2
- package/dist/agy/plugin-installer.js.map +1 -1
- package/dist/codex/lisa-plugin-detection.d.ts +18 -19
- package/dist/codex/lisa-plugin-detection.d.ts.map +1 -1
- package/dist/codex/lisa-plugin-detection.js +26 -22
- package/dist/codex/lisa-plugin-detection.js.map +1 -1
- package/dist/copilot/plugin-installer.d.ts.map +1 -1
- package/dist/copilot/plugin-installer.js +7 -3
- package/dist/copilot/plugin-installer.js.map +1 -1
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +20 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +10 -6
- package/dist/core/lisa.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +2 -2
- package/plugins/lisa/{.codex-plugin → hooks}/hooks.json +7 -7
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +2 -2
- package/plugins/lisa-harper-fabric/{.codex-plugin → hooks}/hooks.json +2 -2
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +2 -2
- package/plugins/lisa-nestjs/{.codex-plugin → hooks}/hooks.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +2 -2
- package/plugins/lisa-rails/{.codex-plugin → hooks}/hooks.json +4 -4
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +2 -2
- package/plugins/lisa-typescript/{.codex-plugin → hooks}/hooks.json +4 -4
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/scripts/generate-codex-plugin-artifacts.mjs +28 -48
- package/scripts/generate-copilot-plugin-artifacts.mjs +5 -0
- package/scripts/generate-cursor-plugin-artifacts.mjs +4 -0
- package/scripts/internal-copilot-runtime-probe.json +10 -4
- package/plugins/lisa/.codex-plugin/hooks/block-no-verify.sh +0 -37
- package/plugins/lisa/.codex-plugin/hooks/inject-flow-context.sh +0 -12
- package/plugins/lisa/.codex-plugin/hooks/inject-rules.sh +0 -33
- package/plugins/lisa/.codex-plugin/hooks/install-pkgs.sh +0 -69
- package/plugins/lisa/.codex-plugin/hooks/notify-ntfy.sh +0 -183
- package/plugins/lisa/.codex-plugin/hooks/setup-jira-cli.sh +0 -51
- package/plugins/lisa-harper-fabric/.codex-plugin/hooks/inject-rules.sh +0 -16
- package/plugins/lisa-nestjs/.codex-plugin/hooks/block-migration-edits.sh +0 -60
- package/plugins/lisa-rails/.codex-plugin/hooks/inject-rules.sh +0 -22
- package/plugins/lisa-rails/.codex-plugin/hooks/rubocop-on-edit.sh +0 -78
- package/plugins/lisa-rails/.codex-plugin/hooks/sg-scan-on-edit.sh +0 -74
- package/plugins/lisa-typescript/.codex-plugin/hooks/block-suppress-directives.sh +0 -73
- package/plugins/lisa-typescript/.codex-plugin/hooks/format-on-edit.sh +0 -79
- package/plugins/lisa-typescript/.codex-plugin/hooks/lint-on-edit.sh +0 -134
- package/plugins/lisa-typescript/.codex-plugin/hooks/sg-scan-on-edit.sh +0 -71
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# This file is managed by Lisa.
|
|
3
|
-
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
-
# =============================================================================
|
|
5
|
-
# RuboCop Lint-and-Format-on-Edit Hook (PostToolUse - Write|Edit)
|
|
6
|
-
# =============================================================================
|
|
7
|
-
# Runs RuboCop -a (safe autocorrect) on each edited Ruby file, then checks for
|
|
8
|
-
# remaining unfixable errors. RuboCop serves as both formatter and linter
|
|
9
|
-
# for Ruby, so this single hook replaces the Prettier + ESLint pipeline.
|
|
10
|
-
#
|
|
11
|
-
# Behavior:
|
|
12
|
-
# - Exit 0: RuboCop passes or auto-fix resolved all errors
|
|
13
|
-
# - Exit 1: unfixable errors remain — blocks Claude so it fixes them immediately
|
|
14
|
-
#
|
|
15
|
-
# @see .claude/rules/verification.md "Self-Correction Loop" section
|
|
16
|
-
# =============================================================================
|
|
17
|
-
|
|
18
|
-
# Extract file path from JSON input
|
|
19
|
-
FILE_PATH=$(cat | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
20
|
-
|
|
21
|
-
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# Check if file type is supported (Ruby only)
|
|
26
|
-
case "${FILE_PATH##*.}" in
|
|
27
|
-
rb) ;;
|
|
28
|
-
*) exit 0 ;;
|
|
29
|
-
esac
|
|
30
|
-
|
|
31
|
-
# Validate project directory
|
|
32
|
-
if [ -z "${CLAUDE_PROJECT_DIR:-}" ]; then
|
|
33
|
-
exit 0
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# Check if file is in a recognized source directory, excluding generated/vendored paths
|
|
37
|
-
RELATIVE_PATH="${FILE_PATH#$CLAUDE_PROJECT_DIR/}"
|
|
38
|
-
case "$RELATIVE_PATH" in
|
|
39
|
-
db/migrate/*|db/schema.rb) exit 0 ;;
|
|
40
|
-
vendor/*|bin/*|tmp/*|node_modules/*) exit 0 ;;
|
|
41
|
-
esac
|
|
42
|
-
case "$RELATIVE_PATH" in
|
|
43
|
-
app/*|lib/*|spec/*|config/*|db/*) ;;
|
|
44
|
-
*) exit 0 ;;
|
|
45
|
-
esac
|
|
46
|
-
|
|
47
|
-
cd "$CLAUDE_PROJECT_DIR" || exit 0
|
|
48
|
-
|
|
49
|
-
# Verify this is a Ruby project with Bundler
|
|
50
|
-
if [ ! -f "Gemfile" ]; then
|
|
51
|
-
exit 0
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
# Run RuboCop autocorrect — fail only on errors (not warnings/conventions)
|
|
55
|
-
echo "Running RuboCop on: $FILE_PATH"
|
|
56
|
-
|
|
57
|
-
# First pass: attempt safe auto-correct
|
|
58
|
-
OUTPUT=$(bundle exec rubocop -a --fail-level E "$FILE_PATH" 2>&1)
|
|
59
|
-
FIX_EXIT=$?
|
|
60
|
-
|
|
61
|
-
if [ $FIX_EXIT -eq 0 ]; then
|
|
62
|
-
echo "RuboCop: No errors in $(basename "$FILE_PATH")"
|
|
63
|
-
exit 0
|
|
64
|
-
fi
|
|
65
|
-
|
|
66
|
-
# Auto-fix resolved some issues but errors remain — re-run to get remaining errors
|
|
67
|
-
OUTPUT=$(bundle exec rubocop --fail-level E "$FILE_PATH" 2>&1)
|
|
68
|
-
LINT_EXIT=$?
|
|
69
|
-
|
|
70
|
-
if [ $LINT_EXIT -eq 0 ]; then
|
|
71
|
-
echo "RuboCop: Auto-fixed all errors in $(basename "$FILE_PATH")"
|
|
72
|
-
exit 0
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
# Unfixable errors remain — block with feedback
|
|
76
|
-
echo "RuboCop found unfixable errors in: $FILE_PATH" >&2
|
|
77
|
-
echo "$OUTPUT" >&2
|
|
78
|
-
exit 1
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# This file is managed by Lisa.
|
|
3
|
-
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
-
# =============================================================================
|
|
5
|
-
# ast-grep Scan-on-Edit Hook (PostToolUse - Write|Edit)
|
|
6
|
-
# =============================================================================
|
|
7
|
-
# Runs ast-grep scan on each edited Ruby file to enforce structural code rules.
|
|
8
|
-
# Complements RuboCop by catching patterns that require AST-level analysis.
|
|
9
|
-
#
|
|
10
|
-
# Behavior:
|
|
11
|
-
# - Exit 0: no issues found or ast-grep not configured
|
|
12
|
-
# - Exit 1: issues found — blocks Claude so it fixes them immediately
|
|
13
|
-
#
|
|
14
|
-
# @see .claude/rules/verification.md "Self-Correction Loop" section
|
|
15
|
-
# =============================================================================
|
|
16
|
-
|
|
17
|
-
# Extract file path from JSON input
|
|
18
|
-
FILE_PATH=$(cat | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
19
|
-
|
|
20
|
-
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
|
|
21
|
-
exit 0
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
# Check if file type is supported (Ruby only)
|
|
25
|
-
case "${FILE_PATH##*.}" in
|
|
26
|
-
rb) ;;
|
|
27
|
-
*) exit 0 ;;
|
|
28
|
-
esac
|
|
29
|
-
|
|
30
|
-
# Validate project directory
|
|
31
|
-
if [ -z "${CLAUDE_PROJECT_DIR:-}" ]; then
|
|
32
|
-
exit 0
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
# Check if file is in a recognized source directory
|
|
36
|
-
RELATIVE_PATH="${FILE_PATH#$CLAUDE_PROJECT_DIR/}"
|
|
37
|
-
case "$RELATIVE_PATH" in
|
|
38
|
-
app/*|lib/*|config/*|spec/*) ;;
|
|
39
|
-
*) exit 0 ;;
|
|
40
|
-
esac
|
|
41
|
-
|
|
42
|
-
cd "$CLAUDE_PROJECT_DIR" || exit 0
|
|
43
|
-
|
|
44
|
-
# Verify ast-grep configuration exists
|
|
45
|
-
if [ ! -f "sgconfig.yml" ]; then
|
|
46
|
-
exit 0
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
# Verify rules are defined
|
|
50
|
-
RULE_COUNT=$(find ast-grep/rules -name "*.yml" -o -name "*.yaml" 2>/dev/null | grep -v ".gitkeep" | wc -l | tr -d ' ')
|
|
51
|
-
if [ "$RULE_COUNT" -eq 0 ]; then
|
|
52
|
-
exit 0
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# Locate ast-grep binary — prefer local sg, then npx fallback
|
|
56
|
-
if command -v sg >/dev/null 2>&1; then
|
|
57
|
-
SG_CMD="sg"
|
|
58
|
-
elif command -v npx >/dev/null 2>&1; then
|
|
59
|
-
SG_CMD="npx @ast-grep/cli"
|
|
60
|
-
else
|
|
61
|
-
echo "ast-grep: sg binary not found, skipping scan"
|
|
62
|
-
exit 0
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
# Run ast-grep scan
|
|
66
|
-
echo "Running ast-grep scan on: $FILE_PATH"
|
|
67
|
-
if OUTPUT=$($SG_CMD scan "$FILE_PATH" 2>&1); then
|
|
68
|
-
echo "ast-grep: No issues found in $(basename "$FILE_PATH")"
|
|
69
|
-
exit 0
|
|
70
|
-
else
|
|
71
|
-
echo "ast-grep found issues in: $FILE_PATH" >&2
|
|
72
|
-
echo "$OUTPUT" >&2
|
|
73
|
-
exit 1
|
|
74
|
-
fi
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# This file is managed by Lisa.
|
|
3
|
-
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
-
#
|
|
5
|
-
# PreToolUse hook (Write|Edit): block adding error-suppression directives to
|
|
6
|
-
# JS/TS source. Suppressing the type checker, linter, or formatter hides real
|
|
7
|
-
# defects instead of fixing them, so it is a documented last resort (see the
|
|
8
|
-
# base "ASK FIRST" governance rule). The agent should stop and get the user's
|
|
9
|
-
# approval rather than slip a suppression past silently.
|
|
10
|
-
#
|
|
11
|
-
# Inspects only the NEW text the tool introduces, scoped to JS/TS files, and
|
|
12
|
-
# matches the directive only in comment syntax (// or /*) so prose, strings,
|
|
13
|
-
# and identifiers that merely mention these tokens are not flagged.
|
|
14
|
-
# Exit code 2 blocks the tool call and surfaces stderr to Claude.
|
|
15
|
-
# Reference: https://docs.claude.com/en/docs/claude-code/hooks
|
|
16
|
-
|
|
17
|
-
JSON_INPUT=$(cat)
|
|
18
|
-
|
|
19
|
-
# Project rule (.claude/rules/PROJECT_RULES.md): never parse JSON in shell with
|
|
20
|
-
# grep/sed/cut/awk — always use jq. Fail open without jq so we never hard-block
|
|
21
|
-
# the agent on missing tooling.
|
|
22
|
-
command -v jq >/dev/null 2>&1 || exit 0
|
|
23
|
-
|
|
24
|
-
FILE_PATH=$(printf '%s' "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
|
|
25
|
-
[ -n "$FILE_PATH" ] || exit 0
|
|
26
|
-
|
|
27
|
-
# Only guard JS/TS source. Directives in other file types (docs, configs that
|
|
28
|
-
# document the rules, this script) are not suppressions.
|
|
29
|
-
case "${FILE_PATH##*.}" in
|
|
30
|
-
ts | tsx | js | jsx | mjs | cjs) ;;
|
|
31
|
-
*) exit 0 ;;
|
|
32
|
-
esac
|
|
33
|
-
|
|
34
|
-
# Resolve the new text per tool shape:
|
|
35
|
-
# Write -> tool_input.content
|
|
36
|
-
# MultiEdit -> tool_input.edits[].new_string
|
|
37
|
-
# Edit -> tool_input.new_string
|
|
38
|
-
NEW_TEXT=$(printf '%s' "$JSON_INPUT" | jq -r '
|
|
39
|
-
.tool_input as $i
|
|
40
|
-
| if ($i.content // null) != null then $i.content
|
|
41
|
-
elif ($i.edits // null) != null then ([$i.edits[].new_string] | join("\n"))
|
|
42
|
-
elif ($i.new_string // null) != null then $i.new_string
|
|
43
|
-
else "" end')
|
|
44
|
-
|
|
45
|
-
# Comment-syntax-only match: a // or /* opener, optional whitespace, then the
|
|
46
|
-
# suppression directive. @ts-expect-error is intentionally NOT matched — it is
|
|
47
|
-
# the safer alternative this hook steers toward.
|
|
48
|
-
DIRECTIVE_RE='(//|/\*)[[:space:]]*(@ts-(ignore|nocheck)|eslint-disable|biome-ignore|prettier-ignore)'
|
|
49
|
-
|
|
50
|
-
if printf '%s' "$NEW_TEXT" | grep -Eq "$DIRECTIVE_RE"; then
|
|
51
|
-
cat >&2 <<MSG
|
|
52
|
-
❌ Blocked: error-suppression directive in $FILE_PATH
|
|
53
|
-
|
|
54
|
-
You are adding a @ts-ignore / @ts-nocheck / eslint-disable / biome-ignore /
|
|
55
|
-
prettier-ignore comment. These silence the type checker, linter, or formatter
|
|
56
|
-
instead of fixing the underlying problem. They are a last resort, not a default.
|
|
57
|
-
|
|
58
|
-
Fix it properly first:
|
|
59
|
-
- Resolve the actual type/lint error rather than suppressing it.
|
|
60
|
-
- Add the missing annotation, narrow the type, or restructure the code so the
|
|
61
|
-
rule passes legitimately.
|
|
62
|
-
- For a faulty dependency type, prefer a typed wrapper or module augmentation.
|
|
63
|
-
|
|
64
|
-
If — and only if — there is genuinely no other way (e.g. a known upstream bug):
|
|
65
|
-
- STOP and get the user's approval before suppressing (base "ASK FIRST" rule).
|
|
66
|
-
- Prefer @ts-expect-error over @ts-ignore (it fails once the error is gone).
|
|
67
|
-
- Scope the disable to one line and one rule, never a whole file.
|
|
68
|
-
- Include a "-- <reason>" description (eslint-comments/require-description).
|
|
69
|
-
MSG
|
|
70
|
-
exit 2
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
exit 0
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# This file is managed by Lisa.
|
|
3
|
-
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
-
|
|
5
|
-
# Hook script to format files with Prettier after Claude edits them
|
|
6
|
-
# This script receives JSON input via stdin with tool information
|
|
7
|
-
# Reference: https://docs.claude.com/en/docs/claude-code/hooks
|
|
8
|
-
|
|
9
|
-
# Read the JSON input from stdin
|
|
10
|
-
JSON_INPUT=$(cat)
|
|
11
|
-
|
|
12
|
-
# Extract the file path from the tool_input. Use jq for robust JSON parsing
|
|
13
|
-
# (never grep/sed/cut — a JSON shape change silently skips formatting). Fail
|
|
14
|
-
# open without jq so we never hard-block an edit.
|
|
15
|
-
command -v jq >/dev/null 2>&1 || exit 0
|
|
16
|
-
FILE_PATH=$(printf '%s' "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
|
|
17
|
-
|
|
18
|
-
# Check if we successfully extracted a file path
|
|
19
|
-
if [ -z "$FILE_PATH" ]; then
|
|
20
|
-
echo "⚠ Skipping Prettier: Could not extract file path from Edit tool input" >&2
|
|
21
|
-
exit 0 # Exit gracefully to not interrupt Claude's workflow
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
# Check if the file exists
|
|
25
|
-
if [ ! -f "$FILE_PATH" ]; then
|
|
26
|
-
echo "⚠ Skipping Prettier: File does not exist: $FILE_PATH" >&2
|
|
27
|
-
exit 0 # Exit gracefully
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
# Get the file extension
|
|
31
|
-
FILE_EXT="${FILE_PATH##*.}"
|
|
32
|
-
|
|
33
|
-
# Check if this is a TypeScript or JavaScript file that should be formatted
|
|
34
|
-
# Based on package.json format command: "prettier --write \"src/**/*.ts\" \"test/**/*.ts\""
|
|
35
|
-
case "$FILE_EXT" in
|
|
36
|
-
ts|tsx|js|jsx|json)
|
|
37
|
-
# File type is supported for formatting
|
|
38
|
-
;;
|
|
39
|
-
*)
|
|
40
|
-
echo "ℹ Skipping Prettier: File type .$FILE_EXT is not configured for auto-formatting"
|
|
41
|
-
exit 0
|
|
42
|
-
;;
|
|
43
|
-
esac
|
|
44
|
-
|
|
45
|
-
# Change to the project directory to ensure package manager commands work
|
|
46
|
-
cd "$CLAUDE_PROJECT_DIR" || exit 0
|
|
47
|
-
|
|
48
|
-
# Resolve Prettier binary — prefer local node_modules/.bin, then package-manager exec
|
|
49
|
-
if [ -x "./node_modules/.bin/prettier" ]; then
|
|
50
|
-
PRETTIER_CMD="./node_modules/.bin/prettier"
|
|
51
|
-
PKG_MANAGER="npm"
|
|
52
|
-
elif [ -f "bun.lockb" ] || [ -f "bun.lock" ]; then
|
|
53
|
-
PRETTIER_CMD="bunx prettier"
|
|
54
|
-
PKG_MANAGER="bun"
|
|
55
|
-
elif [ -f "pnpm-lock.yaml" ]; then
|
|
56
|
-
PRETTIER_CMD="pnpm exec prettier"
|
|
57
|
-
PKG_MANAGER="pnpm"
|
|
58
|
-
elif [ -f "yarn.lock" ]; then
|
|
59
|
-
PRETTIER_CMD="yarn exec prettier"
|
|
60
|
-
PKG_MANAGER="yarn"
|
|
61
|
-
else
|
|
62
|
-
PRETTIER_CMD="npx prettier"
|
|
63
|
-
PKG_MANAGER="npm"
|
|
64
|
-
fi
|
|
65
|
-
|
|
66
|
-
# Run Prettier on the specific file
|
|
67
|
-
echo "🎨 Running Prettier on: $FILE_PATH"
|
|
68
|
-
$PRETTIER_CMD --write "$FILE_PATH" 2>&1 | grep -v "run v" | grep -v "Done in"
|
|
69
|
-
|
|
70
|
-
# Check the exit status
|
|
71
|
-
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
|
72
|
-
echo "✓ Successfully formatted: $(basename "$FILE_PATH")"
|
|
73
|
-
else
|
|
74
|
-
echo "⚠ Prettier formatting failed for: $FILE_PATH" >&2
|
|
75
|
-
echo " You may need to run '$PKG_MANAGER run format' manually to fix formatting issues." >&2
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
# Always exit successfully to not interrupt Claude's workflow
|
|
79
|
-
exit 0
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# This file is managed by Lisa.
|
|
3
|
-
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
-
# =============================================================================
|
|
5
|
-
# Lint-on-Edit Hook (PostToolUse - Write|Edit)
|
|
6
|
-
# =============================================================================
|
|
7
|
-
# Runs oxlint --fix, then ESLint --fix --quiet --cache on each edited
|
|
8
|
-
# TypeScript file. Part of the inline self-correction pipeline:
|
|
9
|
-
# prettier → ast-grep → oxlint --fix → eslint --fix
|
|
10
|
-
#
|
|
11
|
-
# oxlint runs first because it's a Rust-native linter (~1000x faster) that
|
|
12
|
-
# covers the majority of rules, leaving only the slow / type-aware / plugin
|
|
13
|
-
# rules for ESLint to handle.
|
|
14
|
-
#
|
|
15
|
-
# Behavior:
|
|
16
|
-
# - Exit 0: lint passes or auto-fix resolved all errors
|
|
17
|
-
# - Exit 2: unfixable errors remain — blocks Claude so it fixes them immediately
|
|
18
|
-
#
|
|
19
|
-
# @see .claude/rules/verfication.md "Self-Correction Loop" section
|
|
20
|
-
# =============================================================================
|
|
21
|
-
|
|
22
|
-
# Extract file path from JSON input. Use jq for robust JSON parsing (never
|
|
23
|
-
# grep/sed/cut — a shape change would silently skip the blocking lint gate).
|
|
24
|
-
# Fail open without jq so we never hard-block an edit.
|
|
25
|
-
command -v jq >/dev/null 2>&1 || exit 0
|
|
26
|
-
FILE_PATH=$(cat | jq -r '.tool_input.file_path // empty')
|
|
27
|
-
|
|
28
|
-
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
|
|
29
|
-
exit 0
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
# Check if file type is supported (TypeScript only)
|
|
33
|
-
case "${FILE_PATH##*.}" in
|
|
34
|
-
ts|tsx) ;;
|
|
35
|
-
*) exit 0 ;;
|
|
36
|
-
esac
|
|
37
|
-
|
|
38
|
-
# Validate project directory
|
|
39
|
-
if [ -z "${CLAUDE_PROJECT_DIR:-}" ]; then
|
|
40
|
-
exit 0
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
# Check if file is in a source directory
|
|
44
|
-
RELATIVE_PATH="${FILE_PATH#$CLAUDE_PROJECT_DIR/}"
|
|
45
|
-
case "$RELATIVE_PATH" in
|
|
46
|
-
src/*|apps/*|libs/*|test/*|tests/*|features/*|components/*|hooks/*|screens/*|app/*|constants/*|utils/*|providers/*|stores/*) ;;
|
|
47
|
-
*) exit 0 ;;
|
|
48
|
-
esac
|
|
49
|
-
|
|
50
|
-
cd "$CLAUDE_PROJECT_DIR" || exit 0
|
|
51
|
-
|
|
52
|
-
# Resolve oxlint and ESLint binaries — prefer local node_modules/.bin
|
|
53
|
-
if [ -x "./node_modules/.bin/oxlint" ]; then
|
|
54
|
-
OXLINT_CMD="./node_modules/.bin/oxlint"
|
|
55
|
-
elif [ -f "bun.lockb" ] || [ -f "bun.lock" ]; then
|
|
56
|
-
OXLINT_CMD="bunx oxlint"
|
|
57
|
-
elif [ -f "pnpm-lock.yaml" ]; then
|
|
58
|
-
OXLINT_CMD="pnpm exec oxlint"
|
|
59
|
-
elif [ -f "yarn.lock" ]; then
|
|
60
|
-
OXLINT_CMD="yarn exec oxlint"
|
|
61
|
-
else
|
|
62
|
-
OXLINT_CMD="npx oxlint"
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
if [ -x "./node_modules/.bin/eslint" ]; then
|
|
66
|
-
ESLINT_CMD="./node_modules/.bin/eslint"
|
|
67
|
-
elif [ -f "bun.lockb" ] || [ -f "bun.lock" ]; then
|
|
68
|
-
ESLINT_CMD="bunx eslint"
|
|
69
|
-
elif [ -f "pnpm-lock.yaml" ]; then
|
|
70
|
-
ESLINT_CMD="pnpm exec eslint"
|
|
71
|
-
elif [ -f "yarn.lock" ]; then
|
|
72
|
-
ESLINT_CMD="yarn exec eslint"
|
|
73
|
-
else
|
|
74
|
-
ESLINT_CMD="npx eslint"
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
# 1) oxlint --fix (REQUIRED in the Phase 2 hybrid pipeline)
|
|
78
|
-
# If oxlint is missing the project is out of sync with the current Lisa
|
|
79
|
-
# governance — fail loudly rather than silently skipping. ESLint alone is
|
|
80
|
-
# no longer a complete lint pass.
|
|
81
|
-
if [ ! -x "./node_modules/.bin/oxlint" ] && ! command -v "${OXLINT_CMD%% *}" >/dev/null 2>&1; then
|
|
82
|
-
echo "oxlint is required but not installed in this project." >&2
|
|
83
|
-
echo "Add 'oxlint' as a devDependency (Lisa governance pins it via package.lisa.json) and run install." >&2
|
|
84
|
-
exit 2
|
|
85
|
-
fi
|
|
86
|
-
|
|
87
|
-
if [ ! -f ".oxlintrc.json" ] && [ ! -f ".oxlintrc.jsonc" ] && [ ! -f "oxlint.config.ts" ]; then
|
|
88
|
-
echo "oxlint is installed but no .oxlintrc.json found. Run 'lisa update' to install the stack template." >&2
|
|
89
|
-
exit 2
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
echo "Running oxlint --fix on: $FILE_PATH"
|
|
93
|
-
OX_OUTPUT=$($OXLINT_CMD --fix --quiet "$FILE_PATH" 2>&1)
|
|
94
|
-
OX_EXIT=$?
|
|
95
|
-
if [ $OX_EXIT -ne 0 ]; then
|
|
96
|
-
# Re-run without --fix to capture remaining errors
|
|
97
|
-
OX_OUTPUT=$($OXLINT_CMD --quiet "$FILE_PATH" 2>&1)
|
|
98
|
-
OX_EXIT=$?
|
|
99
|
-
if [ $OX_EXIT -ne 0 ]; then
|
|
100
|
-
echo "oxlint found unfixable errors in: $FILE_PATH" >&2
|
|
101
|
-
echo "$OX_OUTPUT" >&2
|
|
102
|
-
exit 2
|
|
103
|
-
fi
|
|
104
|
-
fi
|
|
105
|
-
|
|
106
|
-
# 2) ESLint --fix --quiet --cache
|
|
107
|
-
# --quiet: suppress warnings, only show errors
|
|
108
|
-
# --cache: use ESLint cache for performance
|
|
109
|
-
# --rule: disable no-unused-vars auto-fix to prevent removing imports that Claude
|
|
110
|
-
# plans to use in a subsequent edit (pre-commit hook still catches them)
|
|
111
|
-
echo "Running ESLint --fix on: $FILE_PATH"
|
|
112
|
-
|
|
113
|
-
# First pass: attempt auto-fix
|
|
114
|
-
OUTPUT=$($ESLINT_CMD --fix --quiet --cache --rule '@typescript-eslint/no-unused-vars: off' "$FILE_PATH" 2>&1)
|
|
115
|
-
FIX_EXIT=$?
|
|
116
|
-
|
|
117
|
-
if [ $FIX_EXIT -eq 0 ]; then
|
|
118
|
-
echo "ESLint: No errors in $(basename "$FILE_PATH")"
|
|
119
|
-
exit 0
|
|
120
|
-
fi
|
|
121
|
-
|
|
122
|
-
# Auto-fix resolved some issues but errors remain — re-run to get remaining errors
|
|
123
|
-
OUTPUT=$($ESLINT_CMD --quiet --cache "$FILE_PATH" 2>&1)
|
|
124
|
-
LINT_EXIT=$?
|
|
125
|
-
|
|
126
|
-
if [ $LINT_EXIT -eq 0 ]; then
|
|
127
|
-
echo "ESLint: Auto-fixed all errors in $(basename "$FILE_PATH")"
|
|
128
|
-
exit 0
|
|
129
|
-
fi
|
|
130
|
-
|
|
131
|
-
# Unfixable errors remain — block with feedback
|
|
132
|
-
echo "ESLint found unfixable errors in: $FILE_PATH" >&2
|
|
133
|
-
echo "$OUTPUT" >&2
|
|
134
|
-
exit 2
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# This file is managed by Lisa.
|
|
3
|
-
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
-
|
|
5
|
-
# Hook script to run ast-grep scan after Claude edits files
|
|
6
|
-
# This script receives JSON input via stdin with tool information
|
|
7
|
-
# Reference: https://docs.claude.com/en/docs/claude-code/hooks
|
|
8
|
-
# Note: This hook is BLOCKING - it returns non-zero exit codes so Claude must fix issues
|
|
9
|
-
|
|
10
|
-
# Extract file path from JSON input. Use jq for robust JSON parsing (never
|
|
11
|
-
# grep/sed/cut — a shape change would turn this blocking scan into a silent
|
|
12
|
-
# no-op). Fail open without jq so we never hard-block an edit.
|
|
13
|
-
command -v jq >/dev/null 2>&1 || exit 0
|
|
14
|
-
FILE_PATH=$(cat | jq -r '.tool_input.file_path // empty')
|
|
15
|
-
|
|
16
|
-
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
|
|
17
|
-
exit 0
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
# Check if file type is supported (TypeScript, JavaScript)
|
|
21
|
-
case "${FILE_PATH##*.}" in
|
|
22
|
-
ts|tsx|js|jsx|mjs|cjs) ;;
|
|
23
|
-
*) exit 0 ;;
|
|
24
|
-
esac
|
|
25
|
-
|
|
26
|
-
# Validate project directory
|
|
27
|
-
if [ -z "${CLAUDE_PROJECT_DIR:-}" ]; then
|
|
28
|
-
exit 0
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
# Check if file is in a source directory
|
|
32
|
-
RELATIVE_PATH="${FILE_PATH#$CLAUDE_PROJECT_DIR/}"
|
|
33
|
-
case "$RELATIVE_PATH" in
|
|
34
|
-
src/*|apps/*|libs/*|test/*|tests/*|features/*|components/*|hooks/*|screens/*|app/*|constants/*|utils/*|providers/*|stores/*) ;;
|
|
35
|
-
*) exit 0 ;;
|
|
36
|
-
esac
|
|
37
|
-
|
|
38
|
-
cd "$CLAUDE_PROJECT_DIR" || exit 0
|
|
39
|
-
|
|
40
|
-
# Verify ast-grep configuration exists
|
|
41
|
-
if [ ! -f "sgconfig.yml" ]; then
|
|
42
|
-
exit 0
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
# Verify rules are defined
|
|
46
|
-
RULE_COUNT=$(find ast-grep/rules -name "*.yml" -o -name "*.yaml" 2>/dev/null | grep -v ".gitkeep" | wc -l | tr -d ' ')
|
|
47
|
-
if [ "$RULE_COUNT" -eq 0 ]; then
|
|
48
|
-
exit 0
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
# Detect package manager
|
|
52
|
-
if [ -f "bun.lockb" ] || [ -f "bun.lock" ]; then
|
|
53
|
-
PKG_MANAGER="bun"
|
|
54
|
-
elif [ -f "pnpm-lock.yaml" ]; then
|
|
55
|
-
PKG_MANAGER="pnpm"
|
|
56
|
-
elif [ -f "yarn.lock" ]; then
|
|
57
|
-
PKG_MANAGER="yarn"
|
|
58
|
-
else
|
|
59
|
-
PKG_MANAGER="npm"
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
|
-
# Run ast-grep scan
|
|
63
|
-
echo "Running ast-grep scan on: $FILE_PATH"
|
|
64
|
-
if OUTPUT=$($PKG_MANAGER run sg:scan "$FILE_PATH" 2>&1); then
|
|
65
|
-
echo "ast-grep: No issues found in $(basename "$FILE_PATH")"
|
|
66
|
-
exit 0
|
|
67
|
-
else
|
|
68
|
-
echo "ast-grep found issues in: $FILE_PATH" >&2
|
|
69
|
-
echo "$OUTPUT" >&2
|
|
70
|
-
exit 1
|
|
71
|
-
fi
|