@codyswann/lisa 1.46.4 → 1.47.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.
Files changed (24) hide show
  1. package/all/copy-overwrite/.claude/hooks/verify-completion.sh +77 -0
  2. package/all/copy-overwrite/.claude/rules/lisa.md +2 -1
  3. package/all/copy-overwrite/.claude/rules/verfication.md +55 -0
  4. package/all/copy-overwrite/.claude/settings.json +22 -0
  5. package/package.json +1 -1
  6. package/typescript/copy-contents/.husky/pre-push +113 -102
  7. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +61 -85
  8. package/typescript/copy-overwrite/.claude/settings.json +22 -0
  9. package/typescript/copy-overwrite/.github/workflows/auto-update-pr-branches.yml +15 -1
  10. package/typescript/copy-overwrite/.github/workflows/claude-ci-auto-fix.yml +34 -1
  11. package/typescript/copy-overwrite/.github/workflows/claude-code-review-response.yml +12 -11
  12. package/typescript/copy-overwrite/.github/workflows/claude-deploy-auto-fix.yml +143 -0
  13. package/typescript/copy-overwrite/.github/workflows/claude-nightly-code-complexity.yml +2 -1
  14. package/typescript/copy-overwrite/.github/workflows/claude-nightly-test-coverage.yml +2 -1
  15. package/typescript/copy-overwrite/.github/workflows/claude-nightly-test-improvement.yml +4 -2
  16. package/typescript/copy-overwrite/.github/workflows/claude.yml +2 -1
  17. package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +115 -0
  18. package/typescript/copy-overwrite/.github/workflows/create-issue-on-failure.yml +176 -0
  19. package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +197 -0
  20. package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +269 -0
  21. package/typescript/copy-overwrite/.github/workflows/quality.yml +85 -97
  22. package/typescript/copy-overwrite/audit.ignore.config.json +87 -0
  23. package/typescript/copy-overwrite/eslint.ignore.config.json +4 -1
  24. package/typescript/create-only/audit.ignore.local.json +3 -0
@@ -0,0 +1,77 @@
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
+ # Verification Level Enforcement Hook (Stop)
6
+ # =============================================================================
7
+ # Checks whether the agent declared a verification level when the session
8
+ # involved code changes. Does NOT re-run lint/typecheck/tests (husky does that).
9
+ #
10
+ # Logic:
11
+ # 1. If no Write/Edit tools were used → exit 0 (research/conversation only)
12
+ # 2. If code was written → check last assistant message for verification level
13
+ # 3. If verification level found → exit 0
14
+ # 4. If missing and stop_hook_active is false → block with instructions
15
+ # 5. If missing and stop_hook_active is true → exit 0 (avoid infinite loops)
16
+ #
17
+ # @see .claude/rules/verfication.md "Self-Correction Loop" section
18
+ # =============================================================================
19
+
20
+ # Read JSON input from stdin
21
+ INPUT=$(cat)
22
+
23
+ # Extract transcript path
24
+ TRANSCRIPT_PATH=$(echo "$INPUT" | grep -o '"transcript_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
25
+
26
+ # Exit silently if no transcript available
27
+ if [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
28
+ exit 0
29
+ fi
30
+
31
+ # Check if Write or Edit tools were used during the session
32
+ # Look for tool_use entries with Write or Edit tool names
33
+ if ! grep -q '"tool_name"[[:space:]]*:[[:space:]]*"\(Write\|Edit\|NotebookEdit\)"' "$TRANSCRIPT_PATH" 2>/dev/null; then
34
+ # No code changes — this was research/conversation, allow stop
35
+ exit 0
36
+ fi
37
+
38
+ # Code was written — check if a verification level was declared
39
+ # Extract the last assistant message
40
+ LAST_ASSISTANT=$(awk '/"type"[[:space:]]*:[[:space:]]*"assistant"/{line=$0} END{if(line) print line}' "$TRANSCRIPT_PATH" 2>/dev/null)
41
+
42
+ if [ -z "$LAST_ASSISTANT" ]; then
43
+ exit 0
44
+ fi
45
+
46
+ # Extract the text content from the assistant message
47
+ RESPONSE_TEXT=""
48
+ if command -v jq >/dev/null 2>&1; then
49
+ RESPONSE_TEXT=$(echo "$LAST_ASSISTANT" | jq -r '.message.content[] | select(.type == "text") | .text' 2>/dev/null)
50
+ else
51
+ RESPONSE_TEXT=$(echo "$LAST_ASSISTANT" | grep -o '"text"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*: *"//' | sed 's/"$//')
52
+ fi
53
+
54
+ if [ -z "$RESPONSE_TEXT" ]; then
55
+ exit 0
56
+ fi
57
+
58
+ # Check for verification level keywords (case-insensitive)
59
+ if echo "$RESPONSE_TEXT" | grep -qi "FULLY VERIFIED\|PARTIALLY VERIFIED\|UNVERIFIED"; then
60
+ exit 0
61
+ fi
62
+
63
+ # Check if this is a retry (stop_hook_active flag)
64
+ # The stop_hook_active field is set to true when a Stop hook has already blocked once
65
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | grep -o '"stop_hook_active"[[:space:]]*:[[:space:]]*true' || echo "")
66
+
67
+ if [ -n "$STOP_HOOK_ACTIVE" ]; then
68
+ # Already blocked once — allow stop to prevent infinite loop
69
+ exit 0
70
+ fi
71
+
72
+ # No verification level declared after code changes — block
73
+ cat << 'EOF'
74
+ {"decision":"block","reason":"You changed code but didn't declare a verification level. Run your verification, then declare FULLY VERIFIED, PARTIALLY VERIFIED, or UNVERIFIED with evidence. See .claude/rules/verfication.md for requirements."}
75
+ EOF
76
+
77
+ exit 0
@@ -43,7 +43,8 @@ These directories contain files deployed by Lisa **and** files you create. Do no
43
43
  - `eslint-plugin-code-organization/*`, `eslint-plugin-component-structure/*`, `eslint-plugin-ui-standards/*`
44
44
  - `.claude/settings.json`
45
45
  - `.claude/README.md`
46
- - `.github/workflows/quality.yml`, `.github/workflows/release.yml`, `.github/workflows/claude.yml`, `.github/workflows/claude-ci-auto-fix.yml`, `.github/workflows/claude-code-review-response.yml`, `.github/workflows/claude-nightly-test-improvement.yml`, `.github/workflows/claude-nightly-test-coverage.yml`, `.github/workflows/claude-nightly-code-complexity.yml`, `.github/workflows/auto-update-pr-branches.yml`
46
+ - `.github/workflows/quality.yml`, `.github/workflows/release.yml`, `.github/workflows/claude.yml`, `.github/workflows/claude-ci-auto-fix.yml`, `.github/workflows/claude-deploy-auto-fix.yml`, `.github/workflows/claude-code-review-response.yml`, `.github/workflows/claude-nightly-test-improvement.yml`, `.github/workflows/claude-nightly-test-coverage.yml`, `.github/workflows/claude-nightly-code-complexity.yml`, `.github/workflows/auto-update-pr-branches.yml`
47
+ - `.github/workflows/create-issue-on-failure.yml`, `.github/workflows/create-github-issue-on-failure.yml`, `.github/workflows/create-jira-issue-on-failure.yml`, `.github/workflows/create-sentry-issue-on-failure.yml`
47
48
  - `.github/workflows/build.yml`, `.github/workflows/lighthouse.yml` (Expo)
48
49
  - `.github/workflows/load-test.yml`, `.github/workflows/zap-baseline.yml` (NestJS)
49
50
  - `.github/dependabot.yml`, `.github/GITHUB_ACTIONS.md`, `.github/k6/*`
@@ -150,6 +150,61 @@ Agents must follow this sequence unless explicitly instructed otherwise:
150
150
 
151
151
  ---
152
152
 
153
+ ## Self-Correction Loop
154
+
155
+ Verification is not a one-shot activity. Agents operate within a four-layer self-correction architecture that catches errors at increasing scope. Each layer is enforced automatically — agents do not need to invoke them manually.
156
+
157
+ ### Layer 1 — Inline Correction (PostToolUse)
158
+
159
+ **Trigger:** Every `Write` or `Edit` tool call.
160
+
161
+ **Pipeline:** prettier → ast-grep → eslint (with `--fix --quiet --cache`).
162
+
163
+ Each hook runs on the single file just written. Errors are reported immediately so the agent can fix them before writing more files. This prevents error accumulation across multiple files.
164
+
165
+ - **prettier** formats the file (non-blocking — always exits 0).
166
+ - **ast-grep** scans for structural anti-patterns (blocking — exits 1 on violations).
167
+ - **eslint** auto-fixes what it can, then blocks on unfixable errors (exits 2 on remaining errors).
168
+
169
+ **Agent responsibility:** When a PostToolUse hook blocks, fix the reported errors in the same file before proceeding to other files. Do not accumulate errors.
170
+
171
+ ### Layer 2 — Commit-Time Enforcement (husky pre-commit)
172
+
173
+ **Trigger:** Every `git commit`.
174
+
175
+ **Checks:** lint-staged (eslint + prettier on staged files), gitleaks (secret detection), commitlint (conventional commit format), branch protection (no direct commits to environment branches).
176
+
177
+ This layer catches errors that span multiple files or involve staged-but-not-yet-linted changes. It runs automatically via husky and cannot be bypassed (`--no-verify` is prohibited).
178
+
179
+ ### Layer 3 — Push-Time Enforcement (husky pre-push)
180
+
181
+ **Trigger:** Every `git push`.
182
+
183
+ **Checks:** Full test suite with coverage thresholds, typecheck, security audit, knip (unused exports), integration tests.
184
+
185
+ This layer validates the complete changeset against the project's quality gates. It is the last automated checkpoint before code reaches the remote.
186
+
187
+ ### Layer 4 — Completion Enforcement (Stop hook)
188
+
189
+ **Trigger:** Agent attempts to stop or finish a task.
190
+
191
+ **Check:** If `Write` or `Edit` tools were used during the session, the agent must have declared a verification level (`FULLY VERIFIED`, `PARTIALLY VERIFIED`, or `UNVERIFIED`) in its final message.
192
+
193
+ If no verification level is declared, the Stop hook blocks once with instructions. On retry, it allows the stop to prevent infinite loops.
194
+
195
+ **Agent responsibility:** Before finishing any task that involved code changes, run verification and declare the result with evidence.
196
+
197
+ ### Regeneration Over Patching
198
+
199
+ When the root cause of errors is architectural (wrong abstraction, incorrect data flow, fundamentally broken approach), delete and regenerate rather than incrementally patching. Incremental patches on a broken foundation accumulate tech debt faster than the self-correction loop can catch it.
200
+
201
+ Signs that regeneration is needed:
202
+ - The same file has been edited 3+ times in the same loop without converging
203
+ - Fixing one error introduces another in the same file
204
+ - The fix requires disabling a lint rule or adding a type assertion
205
+
206
+ ---
207
+
153
208
  ## End-User Verification Patterns
154
209
 
155
210
  Agents must choose the pattern that fits the task.
@@ -64,6 +64,10 @@
64
64
  {
65
65
  "type": "command",
66
66
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/sg-scan-on-edit.sh"
67
+ },
68
+ {
69
+ "type": "command",
70
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/lint-on-edit.sh"
67
71
  }
68
72
  ]
69
73
  },
@@ -209,6 +213,24 @@
209
213
  }
210
214
  ],
211
215
  "Stop": [
216
+ {
217
+ "matcher": "",
218
+ "hooks": [
219
+ {
220
+ "type": "command",
221
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/verify-completion.sh"
222
+ }
223
+ ]
224
+ },
225
+ {
226
+ "matcher": "",
227
+ "hooks": [
228
+ {
229
+ "type": "command",
230
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-tired-boss.sh"
231
+ }
232
+ ]
233
+ },
212
234
  {
213
235
  "matcher": "",
214
236
  "hooks": [
package/package.json CHANGED
@@ -95,7 +95,7 @@
95
95
  "axios": ">=1.13.5"
96
96
  },
97
97
  "name": "@codyswann/lisa",
98
- "version": "1.46.4",
98
+ "version": "1.47.1",
99
99
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
100
100
  "main": "dist/index.js",
101
101
  "bin": {
@@ -29,27 +29,86 @@ echo "📦 Using package manager: $PACKAGE_MANAGER"
29
29
  # Run security audit
30
30
  echo "🔒 Running security audit..."
31
31
 
32
- if [ "$PACKAGE_MANAGER" = "yarn" ]; then
33
- # Check if jq is installed (required for yarn audit filtering)
34
- if ! command -v jq >/dev/null 2>&1; then
35
- echo ""
36
- echo "⚠️ WARNING: jq is not installed - required for yarn audit filtering"
37
- echo ""
38
- echo "To install jq:"
39
- echo " macOS: brew install jq"
40
- echo " Windows: choco install jq # or scoop install jq"
41
- echo " Linux: apt-get install jq"
42
- echo ""
43
- echo "Continuing without security audit..."
44
- echo ""
45
- else
46
- # Excluding GHSA-5j98-mcp5-4vw2 (CVE-2025-64756): glob CLI command injection
47
- # This vulnerability only affects the glob CLI (--cmd flag), not library usage
48
- # We only use glob as a library through Babel and other tools - never invoke CLI
49
- # Risk: None - vulnerable code path is not executed in our application
50
- # Run yarn audit and filter for high/critical vulnerabilities (excluding glob CLI vuln)
51
- # Filter by both GHSA ID and CVE ID for robustness
52
- yarn audit --groups dependencies --json | jq -r 'select(.type == "auditAdvisory") | select(.data.advisory.severity == "high" or .data.advisory.severity == "critical") | select((.data.advisory.github_advisory_id == "GHSA-5j98-mcp5-4vw2" or (.data.advisory.cves | any(. == "CVE-2025-64756"))) | not) | .data.advisory' > high_vulns.json
32
+ # jq is required for all audit paths (JSON config reading, npm/yarn filtering)
33
+ if ! command -v jq >/dev/null 2>&1; then
34
+ echo ""
35
+ echo "⚠️ WARNING: jq is not installed - required for security audit"
36
+ echo ""
37
+ echo "To install jq:"
38
+ echo " macOS: brew install jq"
39
+ echo " Windows: choco install jq # or scoop install jq"
40
+ echo " Linux: apt-get install jq"
41
+ echo ""
42
+ echo "Continuing without security audit..."
43
+ echo ""
44
+ else
45
+ # Load GHSA exclusion IDs from JSON config files (managed + project-local)
46
+ load_audit_exclusions() {
47
+ _EXCLUSIONS=""
48
+ for _config_file in audit.ignore.config.json audit.ignore.local.json; do
49
+ if [ -f "$_config_file" ]; then
50
+ _FILE_IDS=$(jq -r '.exclusions[].id' "$_config_file" 2>/dev/null)
51
+ if [ -n "$_FILE_IDS" ]; then
52
+ _EXCLUSIONS="$_EXCLUSIONS $_FILE_IDS"
53
+ fi
54
+ fi
55
+ done
56
+ echo "$_EXCLUSIONS" | tr ' ' '\n' | sort -u | grep -v '^$' | tr '\n' ' '
57
+ }
58
+
59
+ # Load CVE exclusion IDs from JSON config files (for yarn's CVE-based filtering)
60
+ load_audit_cves() {
61
+ _CVES=""
62
+ for _config_file in audit.ignore.config.json audit.ignore.local.json; do
63
+ if [ -f "$_config_file" ]; then
64
+ _FILE_CVES=$(jq -r '.exclusions[] | select(.cve != null) | .cve' "$_config_file" 2>/dev/null)
65
+ if [ -n "$_FILE_CVES" ]; then
66
+ _CVES="$_CVES $_FILE_CVES"
67
+ fi
68
+ fi
69
+ done
70
+ echo "$_CVES" | tr ' ' '\n' | sort -u | grep -v '^$' | tr '\n' ' '
71
+ }
72
+
73
+ AUDIT_EXCLUSIONS=$(load_audit_exclusions)
74
+ AUDIT_CVES=$(load_audit_cves)
75
+
76
+ if [ "$PACKAGE_MANAGER" = "yarn" ]; then
77
+ # Build jq filter for GHSA IDs
78
+ GHSA_FILTER=""
79
+ for _id in $AUDIT_EXCLUSIONS; do
80
+ if [ -n "$GHSA_FILTER" ]; then
81
+ GHSA_FILTER="$GHSA_FILTER or .data.advisory.github_advisory_id == \"$_id\""
82
+ else
83
+ GHSA_FILTER=".data.advisory.github_advisory_id == \"$_id\""
84
+ fi
85
+ done
86
+
87
+ # Build jq filter for CVE IDs
88
+ CVE_FILTER=""
89
+ for _cve in $AUDIT_CVES; do
90
+ if [ -n "$CVE_FILTER" ]; then
91
+ CVE_FILTER="$CVE_FILTER or . == \"$_cve\""
92
+ else
93
+ CVE_FILTER=". == \"$_cve\""
94
+ fi
95
+ done
96
+
97
+ # Combine GHSA and CVE filters
98
+ COMBINED_FILTER=""
99
+ if [ -n "$GHSA_FILTER" ] && [ -n "$CVE_FILTER" ]; then
100
+ COMBINED_FILTER="($GHSA_FILTER or (.data.advisory.cves | any($CVE_FILTER)))"
101
+ elif [ -n "$GHSA_FILTER" ]; then
102
+ COMBINED_FILTER="($GHSA_FILTER)"
103
+ elif [ -n "$CVE_FILTER" ]; then
104
+ COMBINED_FILTER="((.data.advisory.cves | any($CVE_FILTER)))"
105
+ fi
106
+
107
+ if [ -n "$COMBINED_FILTER" ]; then
108
+ yarn audit --groups dependencies --json | jq -r "select(.type == \"auditAdvisory\") | select(.data.advisory.severity == \"high\" or .data.advisory.severity == \"critical\") | select(($COMBINED_FILTER) | not) | .data.advisory" > high_vulns.json
109
+ else
110
+ yarn audit --groups dependencies --json | jq -r 'select(.type == "auditAdvisory") | select(.data.advisory.severity == "high" or .data.advisory.severity == "critical") | .data.advisory' > high_vulns.json
111
+ fi
53
112
 
54
113
  if [ -s high_vulns.json ]; then
55
114
  echo "❌ High or critical vulnerabilities found in production dependencies!"
@@ -60,91 +119,43 @@ if [ "$PACKAGE_MANAGER" = "yarn" ]; then
60
119
 
61
120
  echo "✅ No high or critical vulnerabilities found in production dependencies (excluding known false positives)"
62
121
  rm -f high_vulns.json
63
- fi
64
-
65
- elif [ "$PACKAGE_MANAGER" = "npm" ]; then
66
- # Run npm audit in JSON mode and filter out known false positives before failing.
67
- # npm audit lacks a native --ignore flag, so we parse JSON and exclude by GHSA ID.
68
-
69
- # Excluding GHSA-3ppc-4f35-3m26: minimatch ReDoS via repeated wildcards
70
- # Nested dep in aws-cdk-lib; fix requires minimatch v10 (incompatible with ^3.1.2)
71
- # Risk: None - dev-time CDK tooling, no production runtime exposure
72
-
73
- # Excluding GHSA-2g4f-4pwh-qvx6: ajv ReDoS with $data option
74
- # Nested dep in aws-cdk-lib and eslint; no fix available via npm
75
- # Risk: Low - $data option not used in this application
76
-
77
- AUDIT_JSON=$(npm audit --production --json 2>/dev/null || true)
78
- UNFIXED_HIGH=$(echo "$AUDIT_JSON" | jq '[.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical") | .value.via[] | select(type == "object") | .url | ltrimstr("https://github.com/advisories/")] | unique | map(select(. == "GHSA-3ppc-4f35-3m26" or . == "GHSA-2g4f-4pwh-qvx6" | not)) | length')
79
- if [ "$UNFIXED_HIGH" -gt 0 ]; then
80
- echo "⚠️ Security audit failed. Please fix high/critical vulnerabilities before pushing."
81
- exit 1
82
- fi
83
- echo "✅ No high or critical vulnerabilities found in production dependencies (excluding known false positives)"
84
-
85
- elif [ "$PACKAGE_MANAGER" = "bun" ]; then
86
- # Excluding GHSA-5j98-mcp5-4vw2 (CVE-2025-64756): glob CLI command injection
87
- # This vulnerability only affects the glob CLI (--cmd flag), not library usage
88
- # We only use glob as a library through Babel and other tools - never invoke CLI
89
122
 
90
- # Excluding GHSA-8qq5-rm4j-mr97: node-tar path sanitization vulnerability
91
- # Nested dependency in @expo/cli - bun resolves to patched version but audit still flags it
92
- # Risk: Low - only affects tar extraction with malicious filenames, not our use case
93
-
94
- # Excluding GHSA-37qj-frw5-hhjh: fast-xml-parser RangeError DoS with numeric entities
95
- # Transitive dependency via @react-native-community/cli (Android/iOS build tooling)
96
- # Parent packages pin ^4.4.1; fix requires major version 5.x (incompatible)
97
- # Risk: None - CLI build tool, not a production runtime dependency
98
-
99
- # Excluding GHSA-3ppc-4f35-3m26: minimatch ReDoS via repeated wildcards
100
- # Transitive dependency in devDependencies (eslint, jest, nodemon, ts-morph, etc.)
101
- # Fix requires minimatch v10 which changes export shape (object vs function),
102
- # breaking test-exclude (used by Jest coverage). No production code path is affected.
103
- # Risk: None - only devDependency tooling, never processes untrusted user input
104
-
105
- # Excluding GHSA-jmr7-xgp7-cmfj: fast-xml-parser DoS through entity expansion in DOCTYPE
106
- # Transitive dependency via AWS SDK (@aws-sdk/xml-builder) and snowflake-sdk
107
- # Resolution to >=5.3.6 set in package.json but bun audit still flags intermediate ranges
108
- # Risk: Low - XML parsing of untrusted DOCTYPE content not in our code paths
109
-
110
- # Excluding GHSA-m7jm-9gc2-mpf2: fast-xml-parser entity encoding bypass via regex injection
111
- # Same transitive path as GHSA-jmr7-xgp7-cmfj (AWS SDK, snowflake-sdk)
112
- # Resolution to >=5.3.6 set in package.json but bun audit still flags intermediate ranges
113
- # Risk: Low - no untrusted XML with DOCTYPE entity names processed
114
-
115
- # Excluding GHSA-r6q2-hw4h-h46w: node-tar race condition via Unicode Ligature Collisions on macOS APFS
116
- # Transitive via @nestjs/apollo > @apollo/gateway > make-fetch-happen > cacache > tar
117
- # Resolution to ^7.5.8 set in package.json but bun audit still flags intermediate ranges
118
- # Risk: None - tar extraction not used in production runtime
119
-
120
- # Excluding GHSA-34x7-hfp2-rc4v: node-tar arbitrary file creation via hardlink path traversal
121
- # Same transitive path as GHSA-r6q2-hw4h-h46w
122
- # Risk: None - tar extraction not used in production runtime
123
-
124
- # Excluding GHSA-83g3-92jg-28cx: node-tar arbitrary file read/write via hardlink target escape
125
- # Same transitive path as GHSA-r6q2-hw4h-h46w
126
- # Risk: None - tar extraction not used in production runtime
127
-
128
- # Excluding GHSA-3h5v-q93c-6h6q: ws DoS when handling request with many HTTP headers
129
- # Transitive via @nestjs/graphql, graphql-ws, openai, serverless-offline, serverless-esbuild
130
- # Resolution to ^8.17.1 set in package.json but bun audit still flags intermediate ranges
131
- # Risk: Low - WebSocket servers behind API Gateway which limits headers
132
-
133
- # Excluding GHSA-7r86-cg39-jmmj: minimatch ReDoS via multiple non-adjacent GLOBSTAR segments
134
- # Same transitive dependency chain as GHSA-3ppc-4f35-3m26 (eslint, jest, ts-morph, etc.)
135
- # Fix requires minimatch >=3.1.3 but bun cannot override transitive dependency version ranges
136
- # Risk: None - only devDependency tooling, never processes untrusted user input
123
+ elif [ "$PACKAGE_MANAGER" = "npm" ]; then
124
+ # Build jq exclusion filter for npm audit GHSA IDs
125
+ NPM_EXCLUDE_FILTER=""
126
+ for _id in $AUDIT_EXCLUSIONS; do
127
+ if [ -n "$NPM_EXCLUDE_FILTER" ]; then
128
+ NPM_EXCLUDE_FILTER="$NPM_EXCLUDE_FILTER or . == \"$_id\""
129
+ else
130
+ NPM_EXCLUDE_FILTER=". == \"$_id\""
131
+ fi
132
+ done
133
+
134
+ AUDIT_JSON=$(npm audit --production --json 2>/dev/null || true)
135
+ if [ -n "$NPM_EXCLUDE_FILTER" ]; then
136
+ UNFIXED_HIGH=$(echo "$AUDIT_JSON" | jq "[.vulnerabilities | to_entries[] | select(.value.severity == \"high\" or .value.severity == \"critical\") | .value.via[] | select(type == \"object\") | .url | ltrimstr(\"https://github.com/advisories/\")] | unique | map(select($NPM_EXCLUDE_FILTER | not)) | length")
137
+ else
138
+ UNFIXED_HIGH=$(echo "$AUDIT_JSON" | jq '[.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical") | .value.via[] | select(type == "object") | .url | ltrimstr("https://github.com/advisories/")] | unique | length')
139
+ fi
140
+ if [ "$UNFIXED_HIGH" -gt 0 ]; then
141
+ echo "⚠️ Security audit failed. Please fix high/critical vulnerabilities before pushing."
142
+ exit 1
143
+ fi
144
+ echo "✅ No high or critical vulnerabilities found in production dependencies (excluding known false positives)"
137
145
 
138
- # Excluding GHSA-23c5-xmqv-rm74: minimatch ReDoS via nested *() extglobs
139
- # Same transitive dependency chain as GHSA-3ppc-4f35-3m26 (eslint, jest, ts-morph, etc.)
140
- # Fix requires minimatch >=3.1.3 but bun cannot override transitive dependency version ranges
141
- # Risk: None - only devDependency tooling, never processes untrusted user input
146
+ elif [ "$PACKAGE_MANAGER" = "bun" ]; then
147
+ # Build --ignore flags dynamically from exclusion list
148
+ BUN_IGNORE_FLAGS=""
149
+ for _id in $AUDIT_EXCLUSIONS; do
150
+ BUN_IGNORE_FLAGS="$BUN_IGNORE_FLAGS --ignore $_id"
151
+ done
142
152
 
143
- if ! bun audit --audit-level=high --ignore GHSA-5j98-mcp5-4vw2 --ignore GHSA-8qq5-rm4j-mr97 --ignore GHSA-37qj-frw5-hhjh --ignore GHSA-3ppc-4f35-3m26 --ignore GHSA-jmr7-xgp7-cmfj --ignore GHSA-m7jm-9gc2-mpf2 --ignore GHSA-r6q2-hw4h-h46w --ignore GHSA-34x7-hfp2-rc4v --ignore GHSA-83g3-92jg-28cx --ignore GHSA-3h5v-q93c-6h6q --ignore GHSA-7r86-cg39-jmmj --ignore GHSA-23c5-xmqv-rm74; then
144
- echo "⚠️ Security audit failed. Please fix high/critical vulnerabilities before pushing."
145
- exit 1
153
+ if ! bun audit --audit-level=high $BUN_IGNORE_FLAGS; then
154
+ echo "⚠️ Security audit failed. Please fix high/critical vulnerabilities before pushing."
155
+ exit 1
156
+ fi
157
+ echo "✅ No high or critical vulnerabilities found in production dependencies"
146
158
  fi
147
- echo "✅ No high or critical vulnerabilities found in production dependencies"
148
159
  fi
149
160
 
150
161
  # Run slow lint rules - only if script exists
@@ -1,105 +1,81 @@
1
1
  #!/bin/bash
2
2
  # This file is managed by Lisa.
3
3
  # Do not edit directly — changes will be overwritten on the next `lisa` run.
4
-
5
- # Hook script to lint and auto-fix files with ESLint 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
13
- # The Edit tool input contains a "file_path" field in the tool_input object
14
- FILE_PATH=$(echo "$JSON_INPUT" | grep -o '"tool_input":{[^}]*"file_path":"[^"]*"' | grep -o '"file_path":"[^"]*"' | cut -d'"' -f4)
15
-
16
- # Check if we successfully extracted a file path
17
- if [ -z "$FILE_PATH" ]; then
18
- echo "⚠ Skipping ESLint: Could not extract file path from Edit tool input" >&2
19
- exit 0 # Exit gracefully to not interrupt Claude's workflow
4
+ # =============================================================================
5
+ # ESLint Lint-on-Edit Hook (PostToolUse - Write|Edit)
6
+ # =============================================================================
7
+ # Runs ESLint --fix with --quiet --cache on each edited TypeScript file.
8
+ # Part of the inline self-correction pipeline: prettier → ast-grep → eslint.
9
+ #
10
+ # Behavior:
11
+ # - Exit 0: lint passes or auto-fix resolved all errors
12
+ # - Exit 2: unfixable errors remain blocks Claude so it fixes them immediately
13
+ #
14
+ # @see .claude/rules/verfication.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
20
22
  fi
21
23
 
22
- # Check if the file exists
23
- if [ ! -f "$FILE_PATH" ]; then
24
- echo "⚠ Skipping ESLint: File does not exist: $FILE_PATH" >&2
25
- exit 0 # Exit gracefully
26
- fi
27
-
28
- # Get the file extension
29
- FILE_EXT="${FILE_PATH##*.}"
30
-
31
- # Check if this is a TypeScript file that should be linted
32
- # Based on package.json lint command: "eslint \"{src,apps,libs,test}/**/*.ts\""
33
- case "$FILE_EXT" in
34
- ts|tsx)
35
- # File type is supported for linting
36
- ;;
37
- *)
38
- echo "ℹ Skipping ESLint: File type .$FILE_EXT is not configured for linting"
39
- exit 0
40
- ;;
24
+ # Check if file type is supported (TypeScript only)
25
+ case "${FILE_PATH##*.}" in
26
+ ts|tsx) ;;
27
+ *) exit 0 ;;
41
28
  esac
42
29
 
43
- # Check if the file is in a directory that should be linted
44
- # Extract the relative path from the project directory
45
- RELATIVE_PATH="${FILE_PATH#$CLAUDE_PROJECT_DIR/}"
30
+ # Validate project directory
31
+ if [ -z "${CLAUDE_PROJECT_DIR:-}" ]; then
32
+ exit 0
33
+ fi
46
34
 
47
- # Check if the file is in src, apps, libs, or test directories
35
+ # Check if file is in a source directory
36
+ RELATIVE_PATH="${FILE_PATH#$CLAUDE_PROJECT_DIR/}"
48
37
  case "$RELATIVE_PATH" in
49
- src/*|apps/*|libs/*|test/*|features/*|components/*|hooks/*|screens/*|app/*|constants/*|utils/*|providers/*|stores/*)
50
- # File is in a directory configured for linting
51
- ;;
52
- *)
53
- echo "ℹ Skipping ESLint: File is not in src/, apps/, libs/, or test/ directory"
54
- exit 0
55
- ;;
38
+ src/*|apps/*|libs/*|test/*|tests/*|features/*|components/*|hooks/*|screens/*|app/*|constants/*|utils/*|providers/*|stores/*) ;;
39
+ *) exit 0 ;;
56
40
  esac
57
41
 
58
- # Change to the project directory to ensure package manager commands work
59
42
  cd "$CLAUDE_PROJECT_DIR" || exit 0
60
43
 
61
- # Detect package manager based on lock file presence
62
- detect_package_manager() {
63
- if [ -f "bun.lockb" ] || [ -f "bun.lock" ]; then
64
- echo "bun"
65
- elif [ -f "pnpm-lock.yaml" ]; then
66
- echo "pnpm"
67
- elif [ -f "yarn.lock" ]; then
68
- echo "yarn"
69
- elif [ -f "package-lock.json" ]; then
70
- echo "npm"
71
- else
72
- echo "npm" # Default fallback
73
- fi
74
- }
44
+ # Detect package manager
45
+ if [ -f "bun.lockb" ] || [ -f "bun.lock" ]; then
46
+ PKG_MANAGER="bun"
47
+ elif [ -f "pnpm-lock.yaml" ]; then
48
+ PKG_MANAGER="pnpm"
49
+ elif [ -f "yarn.lock" ]; then
50
+ PKG_MANAGER="yarn"
51
+ else
52
+ PKG_MANAGER="npm"
53
+ fi
75
54
 
76
- PKG_MANAGER=$(detect_package_manager)
55
+ # Run ESLint with --fix --quiet --cache on the specific file
56
+ # --quiet: suppress warnings, only show errors
57
+ # --cache: use ESLint cache for performance
58
+ echo "Running ESLint --fix on: $FILE_PATH"
77
59
 
78
- # Run ESLint with --fix on the specific file
79
- echo "🔍 Running ESLint --fix on: $FILE_PATH"
60
+ # First pass: attempt auto-fix
61
+ OUTPUT=$($PKG_MANAGER eslint --fix --quiet --cache "$FILE_PATH" 2>&1)
62
+ FIX_EXIT=$?
80
63
 
81
- # Run ESLint with fix flag and capture output
82
- $PKG_MANAGER run lint --fix "$FILE_PATH" 2>&1 | while IFS= read -r line; do
83
- # Filter out common noise from package manager output
84
- if [[ ! "$line" =~ ^$ ]] && \
85
- [[ ! "$line" =~ "Need to install the following packages" ]] && \
86
- [[ ! "$line" =~ "Ok to proceed" ]]; then
87
- echo "$line"
88
- fi
89
- done
64
+ if [ $FIX_EXIT -eq 0 ]; then
65
+ echo "ESLint: No errors in $(basename "$FILE_PATH")"
66
+ exit 0
67
+ fi
90
68
 
91
- # Check the exit status (use PIPESTATUS to get the eslint exit code, not the while loop)
92
- EXIT_CODE=${PIPESTATUS[0]}
69
+ # Auto-fix resolved some issues but errors remain re-run to get remaining errors
70
+ OUTPUT=$($PKG_MANAGER eslint --quiet --cache "$FILE_PATH" 2>&1)
71
+ LINT_EXIT=$?
93
72
 
94
- if [ $EXIT_CODE -eq 0 ]; then
95
- echo "ESLint: No issues found in $(basename "$FILE_PATH")"
96
- elif [ $EXIT_CODE -eq 1 ]; then
97
- echo "✓ ESLint: Fixed issues in $(basename "$FILE_PATH")"
98
- echo " Some issues were automatically fixed. Please review the changes."
99
- else
100
- echo "⚠ ESLint found issues that couldn't be auto-fixed in: $FILE_PATH" >&2
101
- echo " You may need to run '$PKG_MANAGER run lint:fix' manually or fix the issues by hand." >&2
73
+ if [ $LINT_EXIT -eq 0 ]; then
74
+ echo "ESLint: Auto-fixed all errors in $(basename "$FILE_PATH")"
75
+ exit 0
102
76
  fi
103
77
 
104
- # Always exit successfully to not interrupt Claude's workflow
105
- exit 0
78
+ # Unfixable errors remain block with feedback
79
+ echo "ESLint found unfixable errors in: $FILE_PATH" >&2
80
+ echo "$OUTPUT" >&2
81
+ exit 2
@@ -63,6 +63,10 @@
63
63
  {
64
64
  "type": "command",
65
65
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/sg-scan-on-edit.sh"
66
+ },
67
+ {
68
+ "type": "command",
69
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/lint-on-edit.sh"
66
70
  }
67
71
  ]
68
72
  },
@@ -208,6 +212,24 @@
208
212
  }
209
213
  ],
210
214
  "Stop": [
215
+ {
216
+ "matcher": "",
217
+ "hooks": [
218
+ {
219
+ "type": "command",
220
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/verify-completion.sh"
221
+ }
222
+ ]
223
+ },
224
+ {
225
+ "matcher": "",
226
+ "hooks": [
227
+ {
228
+ "type": "command",
229
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-tired-boss.sh"
230
+ }
231
+ ]
232
+ },
211
233
  {
212
234
  "matcher": "",
213
235
  "hooks": [