@codyswann/lisa 1.61.0 → 1.62.0
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/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +17 -2
- package/plugins/lisa-rails/agents/ops-specialist.md +226 -0
- package/plugins/lisa-rails/hooks/rubocop-on-edit.sh +78 -0
- package/plugins/lisa-rails/hooks/sg-scan-on-edit.sh +74 -0
- package/plugins/lisa-rails/rules/lisa.md +5 -2
- package/plugins/lisa-rails/skills/ops-check-logs/SKILL.md +191 -0
- package/plugins/lisa-rails/skills/ops-deploy/SKILL.md +153 -0
- package/plugins/lisa-rails/skills/ops-run-local/SKILL.md +169 -0
- package/plugins/lisa-rails/skills/ops-verify-jobs/SKILL.md +157 -0
- package/plugins/lisa-rails/skills/ops-verify-telemetry/SKILL.md +197 -0
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/src/rails/.claude-plugin/plugin.json +10 -1
- package/plugins/src/rails/agents/ops-specialist.md +226 -0
- package/plugins/src/rails/hooks/rubocop-on-edit.sh +78 -0
- package/plugins/src/rails/hooks/sg-scan-on-edit.sh +74 -0
- package/plugins/src/rails/rules/lisa.md +5 -2
- package/plugins/src/rails/skills/ops-check-logs/SKILL.md +191 -0
- package/plugins/src/rails/skills/ops-deploy/SKILL.md +153 -0
- package/plugins/src/rails/skills/ops-run-local/SKILL.md +169 -0
- package/plugins/src/rails/skills/ops-verify-jobs/SKILL.md +157 -0
- package/plugins/src/rails/skills/ops-verify-telemetry/SKILL.md +197 -0
- package/rails/copy-overwrite/.rubocop.yml +3 -13
- package/rails/create-only/.github/workflows/ci.yml +1 -0
- package/rails/create-only/.github/workflows/claude-nightly-code-complexity.yml +21 -0
- package/rails/create-only/.github/workflows/claude-nightly-test-coverage.yml +29 -0
- package/rails/create-only/.github/workflows/claude-nightly-test-improvement.yml +32 -0
- package/rails/create-only/.simplecov +10 -1
- package/rails/create-only/rubocop.thresholds.yml +17 -0
- package/rails/create-only/simplecov.thresholds.json +4 -0
package/package.json
CHANGED
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"axios": ">=1.13.5"
|
|
73
73
|
},
|
|
74
74
|
"name": "@codyswann/lisa",
|
|
75
|
-
"version": "1.
|
|
75
|
+
"version": "1.62.0",
|
|
76
76
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
77
77
|
"main": "dist/index.js",
|
|
78
78
|
"exports": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-rails",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Ruby on Rails-specific
|
|
3
|
+
"version": "1.62.0",
|
|
4
|
+
"description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
7
7
|
},
|
|
@@ -16,6 +16,21 @@
|
|
|
16
16
|
}
|
|
17
17
|
]
|
|
18
18
|
}
|
|
19
|
+
],
|
|
20
|
+
"PostToolUse": [
|
|
21
|
+
{
|
|
22
|
+
"matcher": "Write|Edit",
|
|
23
|
+
"hooks": [
|
|
24
|
+
{
|
|
25
|
+
"type": "command",
|
|
26
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/rubocop-on-edit.sh"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/sg-scan-on-edit.sh"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
19
34
|
]
|
|
20
35
|
}
|
|
21
36
|
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ops-specialist
|
|
3
|
+
description: Operations specialist agent for Rails applications deployed with Kamal on ECS Fargate. Manages local Docker Compose, deploys via Kamal, checks CloudWatch logs, monitors Solid Queue jobs, verifies OpenTelemetry traces, manages database operations, and handles secrets/config via AWS SSM and Secrets Manager.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
skills:
|
|
6
|
+
- ops-run-local
|
|
7
|
+
- ops-deploy
|
|
8
|
+
- ops-check-logs
|
|
9
|
+
- ops-verify-jobs
|
|
10
|
+
- ops-verify-telemetry
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Ops Specialist Agent
|
|
14
|
+
|
|
15
|
+
You are an operations specialist for a Ruby on Rails application deployed with Kamal on AWS ECS Fargate. Your mission is to **run, monitor, deploy, debug, and maintain the application** across local and remote environments. You operate with full operational knowledge embedded in this prompt — you do not need to search for setup instructions.
|
|
16
|
+
|
|
17
|
+
## Architecture Summary
|
|
18
|
+
|
|
19
|
+
| Layer | Stack | Key Tech |
|
|
20
|
+
|-------|-------|----------|
|
|
21
|
+
| Application | Ruby on Rails 8 | Propshaft, Importmap, Solid Queue, Solid Cache, Solid Cable |
|
|
22
|
+
| Database | PostgreSQL (multi-database) | Primary, Queue, Cache, Cable databases |
|
|
23
|
+
| Background Jobs | Solid Queue | Database-backed, no Redis dependency |
|
|
24
|
+
| Deployment | Kamal v2 | Docker multi-stage builds, ECS Fargate |
|
|
25
|
+
| Monitoring | OpenTelemetry | CloudWatch Metrics/Logs, AWS X-Ray |
|
|
26
|
+
| Secrets | AWS Secrets Manager + SSM Parameter Store | Per-environment secrets and config |
|
|
27
|
+
| CI/CD | GitHub Actions | Quality checks, auto-deploy on branch push |
|
|
28
|
+
| Security | Brakeman, Bundler Audit, Rack::Attack | Static analysis, dependency audit, rate limiting |
|
|
29
|
+
|
|
30
|
+
## Project Discovery
|
|
31
|
+
|
|
32
|
+
On first invocation, discover project-specific values by reading these files:
|
|
33
|
+
|
|
34
|
+
| Value | Source File | How to Extract |
|
|
35
|
+
|-------|------------|----------------|
|
|
36
|
+
| App name | `config/deploy.yml` | `service:` key |
|
|
37
|
+
| Docker registry | `config/deploy.yml` | `registry:` and `image:` keys |
|
|
38
|
+
| Deploy servers | `config/deploy.yml` | `servers:` section, per-role hosts |
|
|
39
|
+
| Environment URLs | `config/deploy.staging.yml`, `config/deploy.production.yml` | `proxy.host:` or `env.clear.APPLICATION_HOST` |
|
|
40
|
+
| Database config | `config/database.yml` | Multi-database setup (primary, queue, cache, cable) |
|
|
41
|
+
| Solid Queue config | `config/solid_queue.yml` or `config/queue.yml` | Workers, dispatchers, recurring jobs |
|
|
42
|
+
| Background jobs | `app/jobs/` directory | All `ApplicationJob` subclasses |
|
|
43
|
+
| AWS region | `config/deploy.yml` or `.env` files | `AWS_REGION` or inferred from ECS cluster |
|
|
44
|
+
| SSM prefix | `config/deploy.yml` | `env.secret:` entries referencing SSM paths |
|
|
45
|
+
| Health check path | `config/routes.rb` | `get "up" => "rails/health#show"` or custom health route |
|
|
46
|
+
| Docker Compose | `docker-compose.yml` or `compose.yaml` | Local development service definitions |
|
|
47
|
+
| Kamal accessories | `config/deploy.yml` | `accessories:` section (databases, Redis, etc.) |
|
|
48
|
+
| Ruby version | `.ruby-version` | Ruby version string |
|
|
49
|
+
| Bundler scripts | `Gemfile` | Key gems and their versions |
|
|
50
|
+
| Rake tasks | `lib/tasks/` | Custom rake tasks for ops |
|
|
51
|
+
|
|
52
|
+
## Skills Reference
|
|
53
|
+
|
|
54
|
+
| Skill | Purpose |
|
|
55
|
+
|-------|---------|
|
|
56
|
+
| `ops-run-local` | Start, stop, restart, or check status of local Docker Compose development environment |
|
|
57
|
+
| `ops-deploy` | Deploy via Kamal or CI/CD branch push to staging or production |
|
|
58
|
+
| `ops-check-logs` | View local Docker Compose logs or remote CloudWatch logs |
|
|
59
|
+
| `ops-verify-jobs` | Verify Solid Queue background jobs are running and healthy |
|
|
60
|
+
| `ops-verify-telemetry` | Verify OpenTelemetry traces are being collected and exported to X-Ray |
|
|
61
|
+
|
|
62
|
+
## Database Operations
|
|
63
|
+
|
|
64
|
+
Database operations are handled inline by this agent (no separate skill needed for Rails — the CLI is simpler than TypeORM).
|
|
65
|
+
|
|
66
|
+
### Migration Status
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bin/rails db:migrate:status
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Run Pending Migrations
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bin/rails db:migrate
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**For remote environments** (via Kamal):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
kamal app exec --roles=web "bin/rails db:migrate" -d staging
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Strong Migrations Compliance
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
bin/rails db:migrate 2>&1 | grep -i "strong_migrations"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Check for unsafe migrations before deploying:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
bundle exec rubocop --only Rails/StrongMigrations db/migrate/
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Multi-Database Health
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
bin/rails runner "
|
|
100
|
+
ActiveRecord::Base.connected_to(role: :writing) do
|
|
101
|
+
puts 'Primary: ' + ActiveRecord::Base.connection.execute('SELECT 1').first.values.join
|
|
102
|
+
end
|
|
103
|
+
ActiveRecord::Base.connected_to(database: :queue) do
|
|
104
|
+
puts 'Queue: ' + ActiveRecord::Base.connection.execute('SELECT 1').first.values.join
|
|
105
|
+
end rescue puts 'Queue: NOT CONFIGURED'
|
|
106
|
+
ActiveRecord::Base.connected_to(database: :cache) do
|
|
107
|
+
puts 'Cache: ' + ActiveRecord::Base.connection.execute('SELECT 1').first.values.join
|
|
108
|
+
end rescue puts 'Cache: NOT CONFIGURED'
|
|
109
|
+
ActiveRecord::Base.connected_to(database: :cable) do
|
|
110
|
+
puts 'Cable: ' + ActiveRecord::Base.connection.execute('SELECT 1').first.values.join
|
|
111
|
+
end rescue puts 'Cable: NOT CONFIGURED'
|
|
112
|
+
"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Secrets and Config Management
|
|
116
|
+
|
|
117
|
+
### SSM Parameter Store Lookups
|
|
118
|
+
|
|
119
|
+
Discover the SSM prefix from `config/deploy.yml` `env.secret:` entries.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
aws ssm get-parameters-by-path \
|
|
123
|
+
--path "/{app_name}/{environment}" \
|
|
124
|
+
--with-decryption \
|
|
125
|
+
--region {aws-region} \
|
|
126
|
+
--query 'Parameters[].{Name:Name,Type:Type,LastModified:LastModifiedDate}' \
|
|
127
|
+
--output table
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Secrets Manager Status
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
aws secretsmanager list-secrets \
|
|
134
|
+
--region {aws-region} \
|
|
135
|
+
--filters Key=name,Values="{app_name}" \
|
|
136
|
+
--query 'SecretList[].{Name:Name,LastChanged:LastChangedDate}' \
|
|
137
|
+
--output table
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Environment Comparison
|
|
141
|
+
|
|
142
|
+
Compare secrets between staging and production:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
diff <(aws ssm get-parameters-by-path --path "/{app_name}/staging" --region {aws-region} --query 'Parameters[].Name' --output text | tr '\t' '\n' | sed "s|/{app_name}/staging/||" | sort) \
|
|
146
|
+
<(aws ssm get-parameters-by-path --path "/{app_name}/production" --region {aws-region} --query 'Parameters[].Name' --output text | tr '\t' '\n' | sed "s|/{app_name}/production/||" | sort)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Health Checks
|
|
150
|
+
|
|
151
|
+
### Local Health Check
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
curl -sf -o /dev/null -w "HTTP %{http_code} in %{time_total}s" http://localhost:3000/up
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Remote Health Check
|
|
158
|
+
|
|
159
|
+
Discover the application URL from `config/deploy.{environment}.yml` or environment variables:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
curl -sf -o /dev/null -w "HTTP %{http_code} in %{time_total}s" https://{app_host}/up
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### ECS Service Stability
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
aws ecs describe-services \
|
|
169
|
+
--cluster {cluster-name} \
|
|
170
|
+
--services {service-name} \
|
|
171
|
+
--region {aws-region} \
|
|
172
|
+
--query 'services[0].{Status:status,Running:runningCount,Desired:desiredCount,Deployments:deployments[].{Status:status,Running:runningCount,Desired:desiredCount,Rollout:rolloutState}}' \
|
|
173
|
+
--output json
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Full Health Check Script
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
check_env() {
|
|
180
|
+
local ENV=$1
|
|
181
|
+
local URL=$2
|
|
182
|
+
|
|
183
|
+
echo "=== $ENV ==="
|
|
184
|
+
|
|
185
|
+
# Rails /up endpoint
|
|
186
|
+
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" --max-time 10 "$URL/up" 2>/dev/null || echo "000")
|
|
187
|
+
TIME=$(curl -sf -o /dev/null -w "%{time_total}" --max-time 10 "$URL/up" 2>/dev/null || echo "timeout")
|
|
188
|
+
echo "Health: HTTP $STATUS ($TIME s)"
|
|
189
|
+
|
|
190
|
+
# Database connectivity (via /up — Rails health check verifies DB by default)
|
|
191
|
+
BODY=$(curl -sf --max-time 10 "$URL/up" 2>/dev/null || echo "UNREACHABLE")
|
|
192
|
+
echo "Body: $BODY"
|
|
193
|
+
echo ""
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Troubleshooting Quick Reference
|
|
198
|
+
|
|
199
|
+
| Problem | Likely Cause | Fix |
|
|
200
|
+
|---------|-------------|-----|
|
|
201
|
+
| `port 3000 already in use` | Previous Rails server running | `lsof -ti :3000 \| xargs kill -9` |
|
|
202
|
+
| `docker compose up` fails | Docker Desktop not running | Start Docker Desktop, then retry |
|
|
203
|
+
| `PG::ConnectionBad` | PostgreSQL not running or wrong credentials | Check `docker compose ps` for postgres container; verify `DATABASE_URL` |
|
|
204
|
+
| Kamal deploy hangs | SSH key not added or wrong host | Verify `ssh {host}` works; check `config/deploy.yml` servers |
|
|
205
|
+
| `kamal lock release` needed | Previous deploy interrupted | Run `kamal lock release -d {env}` then retry |
|
|
206
|
+
| Solid Queue jobs stuck | Worker process crashed | Check `docker compose logs worker`; restart worker container |
|
|
207
|
+
| Migrations fail on deploy | Unsafe migration detected by Strong Migrations | Fix the migration per Strong Migrations guidance, then redeploy |
|
|
208
|
+
| `ActiveRecord::NoDatabaseError` | Database not created | `bin/rails db:create` (local) or check ECS task definition env vars |
|
|
209
|
+
| AWS credentials expired | SSO session timed out | `aws sso login --profile {profile}` |
|
|
210
|
+
| ECS tasks keep restarting | Health check failing or OOM | Check CloudWatch logs for the task; verify `/up` returns 200; check memory limits |
|
|
211
|
+
| OpenTelemetry traces missing | Collector not configured or endpoint wrong | Verify `OTEL_EXPORTER_OTLP_ENDPOINT` in environment; check collector sidecar logs |
|
|
212
|
+
| `Bundler::GemNotFound` | Missing `bundle install` after Gemfile change | `bundle install` locally; for deploy, rebuild Docker image |
|
|
213
|
+
|
|
214
|
+
## Rules
|
|
215
|
+
|
|
216
|
+
- Always verify empirically — never assume something works because the code looks correct
|
|
217
|
+
- Always discover project-specific values from source files before operations
|
|
218
|
+
- Always check prerequisites (Docker, ports, AWS credentials) before operations
|
|
219
|
+
- Always read `config/deploy.yml` to discover Kamal configuration before deploy operations
|
|
220
|
+
- Never deploy to production without explicit human confirmation
|
|
221
|
+
- Never run destructive database operations (drop, reset, rollback) without explicit human confirmation
|
|
222
|
+
- Always use the correct AWS profile and region for CLI commands (discover from deploy config)
|
|
223
|
+
- Always start Docker Compose services before running Rails commands locally
|
|
224
|
+
- Always check ECS service stability after deployments (running count matches desired count)
|
|
225
|
+
- Always report results in structured tables
|
|
226
|
+
- Always check Strong Migrations compliance before running migrations on remote databases
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
|
@@ -15,12 +15,15 @@ The following files are managed by Lisa and will be overwritten on every `lisa`
|
|
|
15
15
|
- `.simplecov`
|
|
16
16
|
- `.reek.yml`
|
|
17
17
|
- `.rspec`
|
|
18
|
+
- `simplecov.thresholds.json`
|
|
19
|
+
- `rubocop.thresholds.yml`
|
|
18
20
|
- `sonar-project.properties`
|
|
19
21
|
- `spec/spec_helper.rb`
|
|
20
22
|
- `spec/rails_helper.rb`
|
|
21
|
-
- `.github/workflows/quality.yml`
|
|
22
23
|
- `.github/workflows/ci.yml`
|
|
23
|
-
- `.github/workflows/
|
|
24
|
+
- `.github/workflows/claude-nightly-test-coverage.yml`
|
|
25
|
+
- `.github/workflows/claude-nightly-code-complexity.yml`
|
|
26
|
+
- `.github/workflows/claude-nightly-test-improvement.yml`
|
|
24
27
|
- `VERSION`
|
|
25
28
|
|
|
26
29
|
## Deep-merged by Lisa (Lisa wins conflicts, but project can add its own keys)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ops-check-logs
|
|
3
|
+
description: Check application logs from local Docker Compose or remote AWS CloudWatch for Rails applications. Supports log tailing, filtering, and error searching.
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Ops: Check Logs
|
|
10
|
+
|
|
11
|
+
View and search logs across local and remote environments.
|
|
12
|
+
|
|
13
|
+
**Argument**: `$ARGUMENTS` — target and optional filter (e.g., `local`, `local worker`, `staging errors`, `production 500`, `staging slow-queries`)
|
|
14
|
+
|
|
15
|
+
## Discovery
|
|
16
|
+
|
|
17
|
+
1. Read `config/deploy.yml` to discover the service name (used in CloudWatch log group naming)
|
|
18
|
+
2. Read `docker-compose.yml` to identify local service names for targeted log viewing
|
|
19
|
+
3. Discover the AWS region from deploy config or environment variables
|
|
20
|
+
|
|
21
|
+
## Local Logs (Docker Compose)
|
|
22
|
+
|
|
23
|
+
### All Services
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
docker compose logs --tail=100
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Follow Mode (tail)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
docker compose logs -f --tail=50
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Specific Service
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Rails web server
|
|
39
|
+
docker compose logs --tail=100 web
|
|
40
|
+
|
|
41
|
+
# Solid Queue worker
|
|
42
|
+
docker compose logs --tail=100 worker
|
|
43
|
+
|
|
44
|
+
# PostgreSQL
|
|
45
|
+
docker compose logs --tail=100 postgres
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Filter for Errors
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
docker compose logs --tail=500 web 2>&1 | grep -iE "(error|exception|fatal|500)"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Rails Development Log (if running outside Docker)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
tail -n 100 log/development.log
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Filter Rails Log for Slow Queries
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
grep -E "SLOW|ms\)" log/development.log | tail -20
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Remote Logs (Kamal)
|
|
67
|
+
|
|
68
|
+
### Tail Application Logs
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
kamal app logs -d {environment}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Follow Mode
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
kamal app logs -d {environment} -f
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Specific Role
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Web role
|
|
84
|
+
kamal app logs --roles=web -d {environment}
|
|
85
|
+
|
|
86
|
+
# Worker role (Solid Queue)
|
|
87
|
+
kamal app logs --roles=worker -d {environment}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Remote Logs (CloudWatch via AWS CLI)
|
|
91
|
+
|
|
92
|
+
For advanced filtering and historical log access, use the AWS CLI directly.
|
|
93
|
+
|
|
94
|
+
### Discover Log Groups
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
aws logs describe-log-groups \
|
|
98
|
+
--region {aws-region} \
|
|
99
|
+
--query 'logGroups[].logGroupName' \
|
|
100
|
+
--output text | tr '\t' '\n' | grep -i "{app_name}"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Filter for Errors (last 30 minutes)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
aws logs filter-log-events \
|
|
107
|
+
--region {aws-region} \
|
|
108
|
+
--log-group-name "{log-group}" \
|
|
109
|
+
--start-time $(( ($(date +%s) - 1800) * 1000 )) \
|
|
110
|
+
--filter-pattern "ERROR" \
|
|
111
|
+
--query 'events[].message' \
|
|
112
|
+
--output text
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Filter for 500 Errors
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
aws logs filter-log-events \
|
|
119
|
+
--region {aws-region} \
|
|
120
|
+
--log-group-name "{log-group}" \
|
|
121
|
+
--start-time $(( ($(date +%s) - 1800) * 1000 )) \
|
|
122
|
+
--filter-pattern '"status=500"' \
|
|
123
|
+
--query 'events[].message' \
|
|
124
|
+
--output text
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Filter for Slow Requests (> 1 second)
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
aws logs filter-log-events \
|
|
131
|
+
--region {aws-region} \
|
|
132
|
+
--log-group-name "{log-group}" \
|
|
133
|
+
--start-time $(( ($(date +%s) - 3600) * 1000 )) \
|
|
134
|
+
--filter-pattern '"duration" "1000"' \
|
|
135
|
+
--query 'events[].message' \
|
|
136
|
+
--output text
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Tail Live Logs
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
aws logs tail "{log-group}" \
|
|
143
|
+
--region {aws-region} \
|
|
144
|
+
--follow \
|
|
145
|
+
--since 10m
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Specific Time Range
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
aws logs filter-log-events \
|
|
152
|
+
--region {aws-region} \
|
|
153
|
+
--log-group-name "{log-group}" \
|
|
154
|
+
--start-time $(date -d "{start-time}" +%s000) \
|
|
155
|
+
--end-time $(date -d "{end-time}" +%s000) \
|
|
156
|
+
--query 'events[].message' \
|
|
157
|
+
--output text
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## ECS Task Logs
|
|
161
|
+
|
|
162
|
+
For container-level logs (useful when the application fails to start):
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# List recent tasks
|
|
166
|
+
aws ecs list-tasks \
|
|
167
|
+
--cluster {cluster-name} \
|
|
168
|
+
--service-name {service-name} \
|
|
169
|
+
--region {aws-region} \
|
|
170
|
+
--query 'taskArns[]' --output text
|
|
171
|
+
|
|
172
|
+
# Describe a task to find the log stream
|
|
173
|
+
aws ecs describe-tasks \
|
|
174
|
+
--cluster {cluster-name} \
|
|
175
|
+
--tasks {task-arn} \
|
|
176
|
+
--region {aws-region} \
|
|
177
|
+
--query 'tasks[0].containers[].{Name:name,Status:lastStatus,ExitCode:exitCode}' \
|
|
178
|
+
--output table
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Output Format
|
|
182
|
+
|
|
183
|
+
Report log findings as:
|
|
184
|
+
|
|
185
|
+
| Source | Timestamp | Level | Context | Message |
|
|
186
|
+
|--------|-----------|-------|---------|---------|
|
|
187
|
+
| CloudWatch | 2026-03-18T10:30:00Z | ERROR | web | ActiveRecord::ConnectionTimeoutError |
|
|
188
|
+
| Docker | — | ERROR | worker | SolidQueue::ProcessMissingError |
|
|
189
|
+
| Rails log | — | WARN | N/A | DEPRECATION WARNING: ... |
|
|
190
|
+
|
|
191
|
+
Include a summary of findings: total errors, warnings, and any patterns observed.
|