@arthai/agents 1.0.4 → 1.0.6
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/README.md +55 -3
- package/VERSION +1 -1
- package/agents/troubleshooter.md +132 -0
- package/bin/cli.js +366 -0
- package/bundles/canvas.json +1 -1
- package/bundles/compass.json +1 -1
- package/bundles/counsel.json +1 -0
- package/bundles/cruise.json +1 -1
- package/bundles/forge.json +12 -1
- package/bundles/prism.json +1 -0
- package/bundles/scalpel.json +5 -2
- package/bundles/sentinel.json +8 -2
- package/bundles/shield.json +1 -0
- package/bundles/spark.json +1 -0
- package/compiler.sh +14 -0
- package/dist/plugins/canvas/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/canvas/VERSION +1 -0
- package/dist/plugins/canvas/commands/planning.md +100 -11
- package/dist/plugins/canvas/hooks/hooks.json +16 -0
- package/dist/plugins/canvas/hooks/project-setup.sh +109 -0
- package/dist/plugins/canvas/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/canvas/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/compass/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/compass/VERSION +1 -0
- package/dist/plugins/compass/commands/planning.md +100 -11
- package/dist/plugins/compass/hooks/hooks.json +16 -0
- package/dist/plugins/compass/hooks/project-setup.sh +109 -0
- package/dist/plugins/compass/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/compass/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/counsel/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/counsel/VERSION +1 -0
- package/dist/plugins/counsel/hooks/hooks.json +10 -0
- package/dist/plugins/counsel/hooks/project-setup.sh +109 -0
- package/dist/plugins/counsel/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/counsel/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/cruise/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/cruise/VERSION +1 -0
- package/dist/plugins/cruise/hooks/hooks.json +16 -0
- package/dist/plugins/cruise/hooks/project-setup.sh +109 -0
- package/dist/plugins/cruise/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/cruise/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/forge/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/forge/VERSION +1 -0
- package/dist/plugins/forge/agents/troubleshooter.md +132 -0
- package/dist/plugins/forge/commands/implement.md +99 -1
- package/dist/plugins/forge/commands/planning.md +100 -11
- package/dist/plugins/forge/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/forge/hooks/hooks.json +22 -0
- package/dist/plugins/forge/hooks/project-setup.sh +109 -0
- package/dist/plugins/forge/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/forge/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/prime/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/prime/VERSION +1 -0
- package/dist/plugins/prime/agents/troubleshooter.md +132 -0
- package/dist/plugins/prime/commands/calibrate.md +20 -0
- package/dist/plugins/prime/commands/ci-fix.md +36 -0
- package/dist/plugins/prime/commands/fix.md +23 -0
- package/dist/plugins/prime/commands/implement.md +99 -1
- package/dist/plugins/prime/commands/planning.md +100 -11
- package/dist/plugins/prime/commands/qa-incident.md +54 -0
- package/dist/plugins/prime/commands/restart.md +186 -30
- package/dist/plugins/prime/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/prime/hooks/hooks.json +60 -0
- package/dist/plugins/prime/hooks/post-config-change-restart-reminder.sh +86 -0
- package/dist/plugins/prime/hooks/post-server-crash-watch.sh +120 -0
- package/dist/plugins/prime/hooks/pre-server-port-guard.sh +110 -0
- package/dist/plugins/prime/hooks/project-setup.sh +109 -0
- package/dist/plugins/prime/hooks/sync-agents.sh +99 -12
- package/dist/plugins/prime/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/prime/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/prism/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/prism/VERSION +1 -0
- package/dist/plugins/prism/commands/qa-incident.md +54 -0
- package/dist/plugins/prism/hooks/hooks.json +12 -0
- package/dist/plugins/prism/hooks/project-setup.sh +109 -0
- package/dist/plugins/prism/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/prism/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/scalpel/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/scalpel/VERSION +1 -0
- package/dist/plugins/scalpel/agents/troubleshooter.md +132 -0
- package/dist/plugins/scalpel/commands/ci-fix.md +36 -0
- package/dist/plugins/scalpel/commands/fix.md +23 -0
- package/dist/plugins/scalpel/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/scalpel/hooks/hooks.json +24 -0
- package/dist/plugins/scalpel/hooks/project-setup.sh +109 -0
- package/dist/plugins/scalpel/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/scalpel/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/sentinel/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/sentinel/VERSION +1 -0
- package/dist/plugins/sentinel/agents/troubleshooter.md +132 -0
- package/dist/plugins/sentinel/commands/restart.md +186 -30
- package/dist/plugins/sentinel/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/sentinel/hooks/hooks.json +64 -0
- package/dist/plugins/sentinel/hooks/post-config-change-restart-reminder.sh +86 -0
- package/dist/plugins/sentinel/hooks/post-server-crash-watch.sh +120 -0
- package/dist/plugins/sentinel/hooks/pre-server-port-guard.sh +110 -0
- package/dist/plugins/sentinel/hooks/project-setup.sh +109 -0
- package/dist/plugins/sentinel/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/sentinel/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/shield/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/shield/VERSION +1 -0
- package/dist/plugins/shield/hooks/hooks.json +22 -12
- package/dist/plugins/shield/hooks/project-setup.sh +109 -0
- package/dist/plugins/shield/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/shield/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/spark/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/spark/VERSION +1 -0
- package/dist/plugins/spark/commands/calibrate.md +20 -0
- package/dist/plugins/spark/hooks/hooks.json +10 -0
- package/dist/plugins/spark/hooks/project-setup.sh +109 -0
- package/dist/plugins/spark/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/spark/templates/CLAUDE.md.template +111 -0
- package/hook-defs.json +31 -0
- package/hooks/escalation-guard.sh +177 -0
- package/hooks/post-config-change-restart-reminder.sh +86 -0
- package/hooks/post-server-crash-watch.sh +120 -0
- package/hooks/pre-server-port-guard.sh +110 -0
- package/hooks/project-setup.sh +109 -0
- package/hooks/sync-agents.sh +99 -12
- package/install.sh +2 -2
- package/package.json +1 -1
- package/portable.manifest +7 -1
- package/skills/calibrate/SKILL.md +20 -0
- package/skills/ci-fix/SKILL.md +36 -0
- package/skills/fix/SKILL.md +23 -0
- package/skills/implement/SKILL.md +99 -1
- package/skills/license/SKILL.md +159 -0
- package/skills/planning/SKILL.md +100 -11
- package/skills/publish/SKILL.md +3 -0
- package/skills/qa-incident/SKILL.md +54 -0
- package/skills/restart/SKILL.md +187 -31
|
@@ -1,68 +1,224 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: restart
|
|
3
|
-
description: "
|
|
3
|
+
description: "Discover, restart, and validate local dev servers. Auto-detects Docker vs native, checks health, catches crash loops. Usage: /restart <service> <--preflight>"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Restart Servers Skill
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Discover how services run, restart them safely, and verify they stay healthy.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Phase 1: Discover Services
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**First, check CLAUDE.md** for a `Local Dev Services` table:
|
|
13
13
|
|
|
14
14
|
```markdown
|
|
15
15
|
## Local Dev Services
|
|
16
16
|
|
|
17
|
-
| Service | Port | Directory | Start Command |
|
|
18
|
-
|
|
19
|
-
|
|
|
20
|
-
| Backend | 8000 | backend/ | uvicorn app.main:app --reload
|
|
17
|
+
| Service | Type | Port | Directory | Start Command | Health Check | Depends On |
|
|
18
|
+
|----------|--------|------|-----------|---------------------------------------|-----------------|------------|
|
|
19
|
+
| Postgres | docker | 5432 | — | docker compose up -d postgres | pg_isready | Docker |
|
|
20
|
+
| Backend | native | 8000 | backend/ | uvicorn app.main:app --reload | /api/health | Postgres |
|
|
21
|
+
| Frontend | native | 3000 | frontend/ | npm run dev | /health | Backend |
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
If
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
24
|
+
**If the table is missing or incomplete, auto-discover** by scanning the repo. Check these files in order:
|
|
25
|
+
|
|
26
|
+
| File | What to extract |
|
|
27
|
+
|------|-----------------|
|
|
28
|
+
| `docker-compose.yml` / `docker-compose.*.yml` | Service names, ports, images, healthcheck configs, volume mounts. These are **docker** type services. |
|
|
29
|
+
| `Dockerfile` / `Dockerfile.*` | What gets containerized — cross-reference with compose to understand which services are Docker-managed. |
|
|
30
|
+
| `package.json` (root + subdirs) | `scripts.dev`, `scripts.start`, `scripts.serve` → these are **native** Node services. Check `proxy` field for backend port. |
|
|
31
|
+
| `Makefile` / `Procfile` / `Justfile` | Process definitions, often with ports and dependency ordering. |
|
|
32
|
+
| `pyproject.toml` / `requirements.txt` | Python services — look for uvicorn/gunicorn/flask/django in deps. |
|
|
33
|
+
| `.env` / `.env.local` / `.env.example` | Port assignments (`PORT=`, `DATABASE_URL=`, `REDIS_URL=`), required env vars. |
|
|
34
|
+
| `turbo.json` / `nx.json` / `pnpm-workspace.yaml` | Monorepo structure — maps workspace packages to services. |
|
|
35
|
+
|
|
36
|
+
For each discovered service, determine:
|
|
37
|
+
- **Name**: human-readable (e.g., "Backend API", "Postgres")
|
|
38
|
+
- **Type**: `docker` (managed by docker compose) or `native` (runs directly on host)
|
|
39
|
+
- **Port**: what port it listens on
|
|
40
|
+
- **Directory**: where to `cd` before running the start command
|
|
41
|
+
- **Start command**: exact command to launch it
|
|
42
|
+
- **Health check**: endpoint or command to verify it's running (look for `/health`, `/api/health`, `pg_isready`, `redis-cli ping`, etc.)
|
|
43
|
+
- **Depends on**: other services that must be running first (DB before backend, backend before frontend)
|
|
44
|
+
|
|
45
|
+
**If discovery is ambiguous, ASK the user.** Specifically ask when:
|
|
46
|
+
- Multiple compose files exist and it's unclear which to use (dev vs prod vs test)
|
|
47
|
+
- A service could be Docker OR native (e.g., Postgres has both a compose entry and a local install)
|
|
48
|
+
- No health check endpoint is obvious — ask what URL or command confirms the service is healthy
|
|
49
|
+
- Port conflicts or unusual port assignments are detected
|
|
50
|
+
- You find services but can't determine the dependency order
|
|
51
|
+
|
|
52
|
+
Present what you found and ask for confirmation:
|
|
53
|
+
```
|
|
54
|
+
Found 3 services:
|
|
55
|
+
1. Postgres (docker, port 5432) — via docker-compose.yml
|
|
56
|
+
2. Backend (native, port 8000) — via backend/pyproject.toml
|
|
57
|
+
3. Frontend (native, port 3000) — via frontend/package.json
|
|
58
|
+
|
|
59
|
+
Dependencies: Frontend → Backend → Postgres
|
|
60
|
+
|
|
61
|
+
Health checks:
|
|
62
|
+
- Postgres: pg_isready -h localhost -p 5432
|
|
63
|
+
- Backend: curl localhost:8000/api/health
|
|
64
|
+
- Frontend: curl localhost:3000
|
|
65
|
+
|
|
66
|
+
Does this look right? Any corrections?
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
After confirmation (or if CLAUDE.md table already exists), proceed to Phase 2.
|
|
70
|
+
|
|
71
|
+
**Update CLAUDE.md**: If services were auto-discovered and the user confirmed, write/update the `Local Dev Services` table in CLAUDE.md so future runs skip discovery.
|
|
28
72
|
|
|
29
|
-
|
|
73
|
+
## Phase 2: Pre-flight Validation
|
|
30
74
|
|
|
75
|
+
Before touching any running processes, validate the environment is ready:
|
|
76
|
+
|
|
77
|
+
### 2a. Docker check (if any service type is `docker`)
|
|
78
|
+
```bash
|
|
79
|
+
# Is Docker daemon running?
|
|
80
|
+
docker info > /dev/null 2>&1 && echo "Docker: OK" || echo "Docker: NOT RUNNING"
|
|
81
|
+
```
|
|
82
|
+
If Docker is not running, **stop and tell the user**. Don't proceed.
|
|
83
|
+
|
|
84
|
+
### 2b. Dependency check (for native services)
|
|
31
85
|
```bash
|
|
32
|
-
#
|
|
86
|
+
# Node services — are node_modules installed?
|
|
87
|
+
[ -d "<directory>/node_modules" ] && echo "node_modules: OK" || echo "node_modules: MISSING — run npm install"
|
|
88
|
+
|
|
89
|
+
# Python services — is the venv active / deps installed?
|
|
90
|
+
[ -f "<directory>/.venv/bin/python" ] && echo "venv: OK" || echo "venv: MISSING"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2c. Environment variables
|
|
94
|
+
```bash
|
|
95
|
+
# Check critical env vars exist (read from .env.example or known requirements)
|
|
96
|
+
[ -f "<directory>/.env" ] || [ -f "<directory>/.env.local" ] && echo "env file: OK" || echo "env file: MISSING"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 2d. Port availability
|
|
100
|
+
```bash
|
|
101
|
+
# Check if port is already in use by a DIFFERENT process than expected
|
|
102
|
+
lsof -ti:<port> 2>/dev/null
|
|
103
|
+
```
|
|
104
|
+
If a port is occupied by an unexpected process, warn the user before killing it.
|
|
105
|
+
|
|
106
|
+
### 2e. Pre-flight only mode
|
|
107
|
+
If the user ran `/restart --preflight`, **stop here** and report results. Don't restart anything.
|
|
108
|
+
|
|
109
|
+
## Phase 3: Restart Services (dependency order)
|
|
110
|
+
|
|
111
|
+
Restart in dependency order — infrastructure first, then backends, then frontends.
|
|
112
|
+
|
|
113
|
+
### 3a. Stop services (reverse dependency order)
|
|
114
|
+
```bash
|
|
115
|
+
# For native services — kill by port
|
|
33
116
|
lsof -ti:<port> | xargs kill -9 2>/dev/null
|
|
34
117
|
|
|
118
|
+
# For docker services — stop the specific service
|
|
119
|
+
docker compose stop <service_name>
|
|
120
|
+
|
|
35
121
|
# Wait for ports to free
|
|
36
122
|
sleep 2
|
|
37
|
-
```
|
|
38
123
|
|
|
39
|
-
|
|
124
|
+
# Verify port is actually free
|
|
125
|
+
lsof -ti:<port> 2>/dev/null && echo "WARNING: port <port> still occupied" || echo "Port <port>: free"
|
|
126
|
+
```
|
|
40
127
|
|
|
128
|
+
### 3b. Start services (dependency order)
|
|
41
129
|
```bash
|
|
42
|
-
#
|
|
43
|
-
<
|
|
130
|
+
# Docker services first
|
|
131
|
+
docker compose up -d <service_name>
|
|
132
|
+
|
|
133
|
+
# Then native services — run in background
|
|
134
|
+
cd <directory> && <start_command>
|
|
44
135
|
```
|
|
45
136
|
|
|
46
|
-
Use `run_in_background: true` on the Bash tool.
|
|
137
|
+
Use `run_in_background: true` on the Bash tool for native services.
|
|
138
|
+
|
|
139
|
+
**Wait between dependency layers** — don't start the backend until the DB health check passes. Don't start the frontend until the backend health check passes.
|
|
140
|
+
|
|
141
|
+
## Phase 4: Post-restart Health Validation
|
|
142
|
+
|
|
143
|
+
This is the critical phase. A single health check is not enough — services can start and then crash seconds later.
|
|
47
144
|
|
|
48
|
-
|
|
145
|
+
### 4a. Initial health check (per service, in dependency order)
|
|
49
146
|
|
|
50
147
|
```bash
|
|
51
|
-
#
|
|
52
|
-
curl -sf http://localhost:<port
|
|
148
|
+
# HTTP services
|
|
149
|
+
curl -sf http://localhost:<port><health_path> > /dev/null 2>&1 && echo "<service>: UP" || echo "<service>: DOWN"
|
|
150
|
+
|
|
151
|
+
# Postgres
|
|
152
|
+
pg_isready -h localhost -p <port> 2>/dev/null && echo "Postgres: UP" || echo "Postgres: DOWN"
|
|
153
|
+
|
|
154
|
+
# Redis
|
|
155
|
+
redis-cli -p <port> ping 2>/dev/null | grep -q PONG && echo "Redis: UP" || echo "Redis: DOWN"
|
|
156
|
+
|
|
157
|
+
# Docker services
|
|
158
|
+
docker compose ps <service_name> --format '{{.Status}}' | grep -q "Up" && echo "<service>: UP" || echo "<service>: DOWN"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 4b. Crash loop detection (wait 8 seconds, check again)
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
sleep 8
|
|
165
|
+
|
|
166
|
+
# Re-check each service
|
|
167
|
+
curl -sf http://localhost:<port><health_path> > /dev/null 2>&1 && echo "<service>: STABLE" || echo "<service>: CRASHED"
|
|
168
|
+
|
|
169
|
+
# For native services — is the process still running?
|
|
170
|
+
lsof -ti:<port> > /dev/null 2>&1 && echo "<service> process: alive" || echo "<service> process: GONE"
|
|
53
171
|
```
|
|
54
172
|
|
|
55
|
-
|
|
173
|
+
If a service is down on the second check:
|
|
174
|
+
1. **Check logs** — read the last 30 lines of output for error messages
|
|
175
|
+
2. **Report the failure clearly** with the error
|
|
176
|
+
3. **Do NOT retry automatically** — tell the user what went wrong
|
|
177
|
+
|
|
178
|
+
### 4c. Final status report
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
✅ Restart complete
|
|
182
|
+
|
|
183
|
+
| Service | Type | Port | Status |
|
|
184
|
+
|----------|--------|------|---------|
|
|
185
|
+
| Postgres | docker | 5432 | STABLE |
|
|
186
|
+
| Backend | native | 8000 | STABLE |
|
|
187
|
+
| Frontend | native | 3000 | STABLE |
|
|
188
|
+
|
|
189
|
+
All services healthy after 10s stability check.
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Or if something failed:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
⚠️ Restart partial — 1 service unhealthy
|
|
196
|
+
|
|
197
|
+
| Service | Type | Port | Status |
|
|
198
|
+
|----------|--------|------|---------|
|
|
199
|
+
| Postgres | docker | 5432 | STABLE |
|
|
200
|
+
| Backend | native | 8000 | CRASHED |
|
|
201
|
+
| Frontend | native | 3000 | SKIPPED |
|
|
202
|
+
|
|
203
|
+
Backend crashed after startup. Last error:
|
|
204
|
+
ModuleNotFoundError: No module named 'sqlalchemy'
|
|
205
|
+
|
|
206
|
+
Fix: Run `pip install -r requirements.txt` in backend/ and retry.
|
|
207
|
+
Frontend was skipped because it depends on Backend.
|
|
208
|
+
```
|
|
56
209
|
|
|
57
210
|
## Argument Patterns
|
|
58
211
|
|
|
59
212
|
| User Input | Action |
|
|
60
213
|
|-----------|--------|
|
|
61
|
-
| `/restart` (no args) |
|
|
62
|
-
| `/restart backend` |
|
|
63
|
-
| `/restart
|
|
64
|
-
| `/restart <service>` | Kill and restart the named service |
|
|
214
|
+
| `/restart` (no args) | Discover → validate → restart all services |
|
|
215
|
+
| `/restart backend` | Restart only the named service (and its dependencies if they're down) |
|
|
216
|
+
| `/restart --preflight` | Discover and validate only — don't restart anything |
|
|
65
217
|
|
|
66
|
-
##
|
|
218
|
+
## Key Principles
|
|
67
219
|
|
|
68
|
-
|
|
220
|
+
1. **Never guess** — if you can't determine how a service runs, ask
|
|
221
|
+
2. **Dependency order matters** — always start infra → backend → frontend
|
|
222
|
+
3. **One health check isn't enough** — check twice with a gap to catch crash loops
|
|
223
|
+
4. **Report errors with context** — show the actual log output, not just "DOWN"
|
|
224
|
+
5. **Don't retry blindly** — if a service crashes, diagnose and report, don't loop
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse hook (Bash): Circuit breaker for consecutive failures.
|
|
3
|
+
# Tracks failed commands by error signature. After 3 failures with the same
|
|
4
|
+
# signature, forces KB consultation and outputs a structured stuck report.
|
|
5
|
+
#
|
|
6
|
+
# Uses .claude/.escalation-state.json to persist state across tool calls.
|
|
7
|
+
# stdout is injected as context.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
12
|
+
STATE_FILE="$PROJECT_DIR/.claude/.escalation-state.json"
|
|
13
|
+
CIRCUIT_BREAKER_THRESHOLD=3
|
|
14
|
+
|
|
15
|
+
# Extract the command and exit code
|
|
16
|
+
COMMAND="${CLAUDE_TOOL_INPUT_COMMAND:-}"
|
|
17
|
+
if [ -z "$COMMAND" ] && [ -n "${CLAUDE_TOOL_INPUT:-}" ]; then
|
|
18
|
+
COMMAND=$(echo "$CLAUDE_TOOL_INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('command',''))" 2>/dev/null) || true
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
EXIT_CODE="${CLAUDE_TOOL_RESULT_EXIT_CODE:-0}"
|
|
22
|
+
|
|
23
|
+
[ -z "$COMMAND" ] && exit 0
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Skip tracking for non-failing commands and read-only commands
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
# Success — reset state if we had failures for this signature
|
|
30
|
+
if [ "$EXIT_CODE" = "0" ]; then
|
|
31
|
+
if [ -f "$STATE_FILE" ]; then
|
|
32
|
+
# Clear state on success — the issue is resolved
|
|
33
|
+
python3 -c "
|
|
34
|
+
import json, sys
|
|
35
|
+
try:
|
|
36
|
+
with open('$STATE_FILE', 'r') as f:
|
|
37
|
+
state = json.load(f)
|
|
38
|
+
# Reset consecutive failures
|
|
39
|
+
state['consecutive_failures'] = 0
|
|
40
|
+
state['last_error_signature'] = ''
|
|
41
|
+
state['attempts'] = []
|
|
42
|
+
with open('$STATE_FILE', 'w') as f:
|
|
43
|
+
json.dump(state, f, indent=2)
|
|
44
|
+
except:
|
|
45
|
+
pass
|
|
46
|
+
" 2>/dev/null || true
|
|
47
|
+
fi
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Skip tracking for read-only commands (ls, cat, grep, git status, etc.)
|
|
52
|
+
if echo "$COMMAND" | grep -qE '^\s*(ls|cat|head|tail|grep|rg|git\s+(status|log|diff|show|branch)|echo|pwd|which|type|file|wc)\b'; then
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# Compute error signature from command + exit code
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
# Get the last few lines of error output (from CLAUDE_TOOL_RESULT_STDERR or infer)
|
|
61
|
+
ERROR_OUTPUT="${CLAUDE_TOOL_RESULT_STDERR:-}"
|
|
62
|
+
if [ -z "$ERROR_OUTPUT" ] && [ -n "${CLAUDE_TOOL_RESULT_STDOUT:-}" ]; then
|
|
63
|
+
# Sometimes errors go to stdout (e.g., npm, python)
|
|
64
|
+
ERROR_OUTPUT=$(echo "${CLAUDE_TOOL_RESULT_STDOUT:-}" | tail -5)
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Compute signature: hash of (command_base + error_pattern)
|
|
68
|
+
# Strip variable parts (paths, timestamps, PIDs) for stable signatures
|
|
69
|
+
COMMAND_BASE=$(echo "$COMMAND" | awk '{print $1, $2}')
|
|
70
|
+
ERROR_PATTERN=$(echo "$ERROR_OUTPUT" | sed 's/[0-9]\{4,\}//g; s|/[^ ]*||g' | head -3)
|
|
71
|
+
SIGNATURE=$(echo "${COMMAND_BASE}:${ERROR_PATTERN}" | shasum -a 256 | cut -c1-16)
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Update state and check circuit breaker
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
RESULT=$(python3 -c "
|
|
78
|
+
import json, sys, time, os
|
|
79
|
+
|
|
80
|
+
state_file = '$STATE_FILE'
|
|
81
|
+
signature = '$SIGNATURE'
|
|
82
|
+
command = '''$COMMAND'''[:200]
|
|
83
|
+
error = '''$ERROR_OUTPUT'''[:500]
|
|
84
|
+
threshold = $CIRCUIT_BREAKER_THRESHOLD
|
|
85
|
+
|
|
86
|
+
# Load or create state
|
|
87
|
+
state = {'consecutive_failures': 0, 'last_error_signature': '', 'attempts': [], 'total_circuits_tripped': 0}
|
|
88
|
+
try:
|
|
89
|
+
if os.path.exists(state_file):
|
|
90
|
+
with open(state_file, 'r') as f:
|
|
91
|
+
state = json.load(f)
|
|
92
|
+
except:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# Check if same signature as last failure
|
|
96
|
+
if state.get('last_error_signature') == signature:
|
|
97
|
+
state['consecutive_failures'] = state.get('consecutive_failures', 0) + 1
|
|
98
|
+
else:
|
|
99
|
+
# New error signature — reset counter
|
|
100
|
+
state['consecutive_failures'] = 1
|
|
101
|
+
state['attempts'] = []
|
|
102
|
+
|
|
103
|
+
state['last_error_signature'] = signature
|
|
104
|
+
state['attempts'] = (state.get('attempts', []) + [{'command': command, 'error': error[:200], 'time': time.time()}])[-5:]
|
|
105
|
+
|
|
106
|
+
# Check threshold
|
|
107
|
+
tripped = state['consecutive_failures'] >= threshold
|
|
108
|
+
|
|
109
|
+
if tripped:
|
|
110
|
+
state['total_circuits_tripped'] = state.get('total_circuits_tripped', 0) + 1
|
|
111
|
+
|
|
112
|
+
# Save state
|
|
113
|
+
os.makedirs(os.path.dirname(state_file), exist_ok=True)
|
|
114
|
+
with open(state_file, 'w') as f:
|
|
115
|
+
json.dump(state, f, indent=2)
|
|
116
|
+
|
|
117
|
+
# Output: tripped|count|attempts_summary
|
|
118
|
+
attempts_summary = ' / '.join([a.get('command', '')[:60] for a in state.get('attempts', [])])
|
|
119
|
+
print(f\"{'TRIPPED' if tripped else 'OK'}|{state['consecutive_failures']}|{attempts_summary}\")
|
|
120
|
+
" 2>/dev/null) || exit 0
|
|
121
|
+
|
|
122
|
+
STATUS=$(echo "$RESULT" | cut -d'|' -f1)
|
|
123
|
+
COUNT=$(echo "$RESULT" | cut -d'|' -f2)
|
|
124
|
+
ATTEMPTS=$(echo "$RESULT" | cut -d'|' -f3-)
|
|
125
|
+
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
# If not tripped, show warning at count 2
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
if [ "$STATUS" = "OK" ] && [ "$COUNT" = "2" ]; then
|
|
131
|
+
echo "ESCALATION WARNING: 2 consecutive failures with same error pattern."
|
|
132
|
+
echo "One more failure triggers the circuit breaker."
|
|
133
|
+
echo "Before retrying: check .claude/knowledge/ for known solutions."
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
# Circuit breaker tripped — inject structured stuck report
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
if [ "$STATUS" = "TRIPPED" ]; then
|
|
142
|
+
echo "CIRCUIT BREAKER TRIPPED — $COUNT consecutive failures with same error signature."
|
|
143
|
+
echo ""
|
|
144
|
+
echo "STOP. Do not attempt another fix without completing these steps:"
|
|
145
|
+
echo ""
|
|
146
|
+
echo "1. CONSULT KNOWLEDGE BASE:"
|
|
147
|
+
echo " - Read .claude/knowledge/shared/conventions.md"
|
|
148
|
+
echo " - Read .claude/knowledge/qa-knowledge/ (search for error keywords)"
|
|
149
|
+
echo " - Run: git log --all --grep='fix:' --oneline -10"
|
|
150
|
+
echo ""
|
|
151
|
+
echo "2. GATHER EVIDENCE (if not already done):"
|
|
152
|
+
echo " - Read full error output (not just the last line)"
|
|
153
|
+
echo " - Check environment: env vars, ports (lsof), processes (ps), disk (df)"
|
|
154
|
+
echo " - Check dependencies: node_modules, venv, Docker containers"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "3. IF STILL STUCK — use this template to ask for help:"
|
|
157
|
+
echo " ┌─────────────────────────────────────────────────────────┐"
|
|
158
|
+
echo " │ STUCK REPORT │"
|
|
159
|
+
echo " │ │"
|
|
160
|
+
echo " │ Error: [exact error message] │"
|
|
161
|
+
echo " │ Context: [what I was doing] │"
|
|
162
|
+
echo " │ Attempts: │"
|
|
163
|
+
echo " │ 1. [what I tried] -> [result] │"
|
|
164
|
+
echo " │ 2. [what I tried] -> [result] │"
|
|
165
|
+
echo " │ 3. [what I tried] -> [result] │"
|
|
166
|
+
echo " │ Evidence: [logs, state, KB search results] │"
|
|
167
|
+
echo " │ What I need: [access/data/decision] │"
|
|
168
|
+
echo " │ My recommendation: [option A because X] │"
|
|
169
|
+
echo " └─────────────────────────────────────────────────────────┘"
|
|
170
|
+
echo ""
|
|
171
|
+
echo "4. IF ON A TEAM — escalate to troubleshooter agent or ask a teammate."
|
|
172
|
+
echo " If solo — present the stuck report to the user with options."
|
|
173
|
+
echo ""
|
|
174
|
+
echo "Previous attempts: $ATTEMPTS"
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
exit 0
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/project-setup.sh",
|
|
10
|
+
"timeout": 10
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
3
15
|
"PostToolUse": [
|
|
4
16
|
{
|
|
5
17
|
"matcher": "Bash",
|
|
@@ -20,6 +32,58 @@
|
|
|
20
32
|
"timeout": 5
|
|
21
33
|
}
|
|
22
34
|
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"matcher": "Edit",
|
|
38
|
+
"hooks": [
|
|
39
|
+
{
|
|
40
|
+
"type": "command",
|
|
41
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/post-config-change-restart-reminder.sh",
|
|
42
|
+
"timeout": 5
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"matcher": "Write",
|
|
48
|
+
"hooks": [
|
|
49
|
+
{
|
|
50
|
+
"type": "command",
|
|
51
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/post-config-change-restart-reminder.sh",
|
|
52
|
+
"timeout": 5
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"matcher": "Bash",
|
|
58
|
+
"hooks": [
|
|
59
|
+
{
|
|
60
|
+
"type": "command",
|
|
61
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/post-server-crash-watch.sh",
|
|
62
|
+
"timeout": 15
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"matcher": "Bash",
|
|
68
|
+
"hooks": [
|
|
69
|
+
{
|
|
70
|
+
"type": "command",
|
|
71
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/escalation-guard.sh",
|
|
72
|
+
"timeout": 5
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"PreToolUse": [
|
|
78
|
+
{
|
|
79
|
+
"matcher": "Bash",
|
|
80
|
+
"hooks": [
|
|
81
|
+
{
|
|
82
|
+
"type": "command",
|
|
83
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-server-port-guard.sh",
|
|
84
|
+
"timeout": 5
|
|
85
|
+
}
|
|
86
|
+
]
|
|
23
87
|
}
|
|
24
88
|
]
|
|
25
89
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse hook (Edit/Write): Remind to restart after config file changes.
|
|
3
|
+
# Detects modifications to docker-compose, .env, Dockerfile, nginx, and port
|
|
4
|
+
# configs, then reminds the user that a /restart may be needed.
|
|
5
|
+
#
|
|
6
|
+
# stdout is injected as context.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# Extract the file path from tool input
|
|
11
|
+
FILE_PATH="${CLAUDE_TOOL_INPUT_FILE_PATH:-}"
|
|
12
|
+
if [ -z "$FILE_PATH" ] && [ -n "${CLAUDE_TOOL_INPUT:-}" ]; then
|
|
13
|
+
FILE_PATH=$(echo "$CLAUDE_TOOL_INPUT" | python3 -c "import json,sys; print(json.load(sys.stdin).get('file_path',''))" 2>/dev/null) || true
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Check if the modified file is a config that affects running services
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
BASENAME=$(basename "$FILE_PATH")
|
|
23
|
+
NEEDS_RESTART=false
|
|
24
|
+
REASON=""
|
|
25
|
+
|
|
26
|
+
case "$BASENAME" in
|
|
27
|
+
docker-compose.yml|docker-compose.yaml|docker-compose.*.yml|docker-compose.*.yaml|compose.yml|compose.yaml)
|
|
28
|
+
NEEDS_RESTART=true
|
|
29
|
+
REASON="Docker Compose config changed — containers may need rebuilding"
|
|
30
|
+
;;
|
|
31
|
+
.env|.env.local|.env.development|.env.production)
|
|
32
|
+
NEEDS_RESTART=true
|
|
33
|
+
REASON="Environment variables changed — servers need restart to pick up new values"
|
|
34
|
+
;;
|
|
35
|
+
Dockerfile|Dockerfile.*)
|
|
36
|
+
NEEDS_RESTART=true
|
|
37
|
+
REASON="Dockerfile changed — container needs rebuilding"
|
|
38
|
+
;;
|
|
39
|
+
nginx.conf|nginx.*.conf)
|
|
40
|
+
NEEDS_RESTART=true
|
|
41
|
+
REASON="Nginx config changed — needs reload"
|
|
42
|
+
;;
|
|
43
|
+
next.config.js|next.config.mjs|next.config.ts)
|
|
44
|
+
NEEDS_RESTART=true
|
|
45
|
+
REASON="Next.js config changed — dev server needs restart"
|
|
46
|
+
;;
|
|
47
|
+
vite.config.js|vite.config.ts|vite.config.mjs)
|
|
48
|
+
NEEDS_RESTART=true
|
|
49
|
+
REASON="Vite config changed — dev server needs restart"
|
|
50
|
+
;;
|
|
51
|
+
tsconfig.json|tsconfig.*.json)
|
|
52
|
+
NEEDS_RESTART=true
|
|
53
|
+
REASON="TypeScript config changed — dev server may need restart"
|
|
54
|
+
;;
|
|
55
|
+
webpack.config.js|webpack.config.ts)
|
|
56
|
+
NEEDS_RESTART=true
|
|
57
|
+
REASON="Webpack config changed — dev server needs restart"
|
|
58
|
+
;;
|
|
59
|
+
package.json)
|
|
60
|
+
NEEDS_RESTART=true
|
|
61
|
+
REASON="package.json changed — may need npm install + restart"
|
|
62
|
+
;;
|
|
63
|
+
pyproject.toml|setup.py|setup.cfg|requirements.txt|requirements.*.txt)
|
|
64
|
+
NEEDS_RESTART=true
|
|
65
|
+
REASON="Python dependencies changed — may need pip install + restart"
|
|
66
|
+
;;
|
|
67
|
+
Procfile|Procfile.*)
|
|
68
|
+
NEEDS_RESTART=true
|
|
69
|
+
REASON="Procfile changed — process definitions updated"
|
|
70
|
+
;;
|
|
71
|
+
esac
|
|
72
|
+
|
|
73
|
+
# Also check for port-related changes in any file
|
|
74
|
+
if [ "$NEEDS_RESTART" = "false" ]; then
|
|
75
|
+
# Check if the file path contains common config directories
|
|
76
|
+
if echo "$FILE_PATH" | grep -qE '(config/|conf/|\.config)'; then
|
|
77
|
+
NEEDS_RESTART=true
|
|
78
|
+
REASON="Config file modified — services may need restart"
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
$NEEDS_RESTART || exit 0
|
|
83
|
+
|
|
84
|
+
echo "CONFIG CHANGED — $REASON. Run /restart to apply."
|
|
85
|
+
|
|
86
|
+
exit 0
|