bigpowers 2.11.0 → 2.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/package.json +2 -2
- package/.pi/prompts/smoke-test.md +190 -0
- package/.pi/skills/smoke-test/SKILL.md +192 -0
- package/CHANGELOG.md +14 -0
- package/SKILL-INDEX.md +37 -36
- package/package.json +1 -1
- package/scripts/generate-skill-index.sh +1 -0
- package/scripts/run-smoke.sh +233 -0
- package/skills-lock.json +5 -0
- package/smoke-test/SKILL.md +191 -0
package/.pi/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bigpowers",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.12.1",
|
|
4
|
+
"description": "66 skills — 61 agent skills for spec-driven, test-first software development by solo developers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
7
7
|
],
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Post-deploy health-check against a live URL. Validates HTTP status, response content, and critical endpoints. Runnable standalone OR as the final step of the deploy skill."
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Smoke Test
|
|
7
|
+
|
|
8
|
+
> **HARD GATE** — Do NOT run smoke-test against a URL that hasn't been deployed yet. Always run `deploy` first, then `smoke-test`.
|
|
9
|
+
>
|
|
10
|
+
> **HARD GATE** — A failed smoke test means the deployment is broken. Do NOT mark a deploy as successful until all smoke checks pass.
|
|
11
|
+
|
|
12
|
+
Validate a deployed application is healthy by running a configurable set of HTTP checks against live URLs. Each check asserts:
|
|
13
|
+
- HTTP status code (e.g., 200 for success, 404 for expected-not-found)
|
|
14
|
+
- Response body content signal (regex or jq expression)
|
|
15
|
+
- Response time threshold (optional)
|
|
16
|
+
|
|
17
|
+
Can be run standalone for quick health checks or chained as the final step of the `deploy` skill.
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
Smoke checks are defined in `smoke-checks.yaml` at the project root:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
# smoke-checks.yaml — auto-loaded if present at project root
|
|
25
|
+
base_url: "https://example.com"
|
|
26
|
+
checks:
|
|
27
|
+
- name: "Homepage"
|
|
28
|
+
path: "/"
|
|
29
|
+
method: GET
|
|
30
|
+
expected_status: 200
|
|
31
|
+
content_signal: "bigpowers"
|
|
32
|
+
max_response_time_ms: 3000
|
|
33
|
+
|
|
34
|
+
- name: "API Health"
|
|
35
|
+
path: "/api/health"
|
|
36
|
+
method: GET
|
|
37
|
+
expected_status: 200
|
|
38
|
+
content_signal: "ok|healthy"
|
|
39
|
+
|
|
40
|
+
- name: "API Jogos"
|
|
41
|
+
path: "/api/jogos"
|
|
42
|
+
method: GET
|
|
43
|
+
expected_status: 200
|
|
44
|
+
content_signal: "jogos|games"
|
|
45
|
+
|
|
46
|
+
- name: "Not Found handling"
|
|
47
|
+
path: "/nonexistent"
|
|
48
|
+
method: GET
|
|
49
|
+
expected_status: 404
|
|
50
|
+
content_signal: "not found|404"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Checks can also be specified inline via environment variables or CLI arguments for ad-hoc use.
|
|
54
|
+
|
|
55
|
+
### Check Schema
|
|
56
|
+
|
|
57
|
+
| Field | Required | Default | Description |
|
|
58
|
+
|-------|----------|---------|-------------|
|
|
59
|
+
| `name` | Yes | — | Human-readable check name (used in report) |
|
|
60
|
+
| `path` | Yes | `/` | URL path relative to base_url |
|
|
61
|
+
| `method` | No | `GET` | HTTP method |
|
|
62
|
+
| `expected_status` | No | `200` | Expected HTTP status code |
|
|
63
|
+
| `content_signal` | No | — | Regex or string to find in response body |
|
|
64
|
+
| `max_response_time_ms` | No | — | Fail if response slower than this threshold (ms) |
|
|
65
|
+
|
|
66
|
+
## Process
|
|
67
|
+
|
|
68
|
+
### 1. Load smoke checks
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
SMOKE_CHECKS_FILE="${SMOKE_CHECKS_FILE:-smoke-checks.yaml}"
|
|
72
|
+
BASE_URL="${DEPLOY_URL:-$BASE_URL}"
|
|
73
|
+
|
|
74
|
+
if [ -f "$SMOKE_CHECKS_FILE" ]; then
|
|
75
|
+
echo "Loaded smoke checks from $SMOKE_CHECKS_FILE"
|
|
76
|
+
elif [ -n "$BASE_URL" ]; then
|
|
77
|
+
echo "No smoke-checks.yaml found. Using single URL check against $BASE_URL"
|
|
78
|
+
else
|
|
79
|
+
echo "ERROR: No smoke-checks.yaml found and no DEPLOY_URL/BASE_URL set."
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2. Run each check
|
|
85
|
+
|
|
86
|
+
For each check in the configuration, perform an HTTP request:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
url="${BASE_URL}${path}"
|
|
90
|
+
start_time=$(python3 -c 'import time; print(int(time.time() * 1000))')
|
|
91
|
+
|
|
92
|
+
# Perform the HTTP request
|
|
93
|
+
response=$(curl -s -o /tmp/smoke_body.txt -w "%{http_code}" "$url")
|
|
94
|
+
response_time=$(( $(python3 -c 'import time; print(int(time.time() * 1000))') - start_time ))
|
|
95
|
+
status=$response
|
|
96
|
+
body=$(cat /tmp/smoke_body.txt)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. Assert results
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
checks_passed=0
|
|
103
|
+
checks_failed=0
|
|
104
|
+
failures=""
|
|
105
|
+
|
|
106
|
+
# Assert status code
|
|
107
|
+
if [ "$status" -ne "${expected_status:-200}" ]; then
|
|
108
|
+
echo " FAIL: expected status ${expected_status} but got $status"
|
|
109
|
+
checks_failed=$((checks_failed + 1))
|
|
110
|
+
failures="${failures} - $name: HTTP $status (expected ${expected_status})\n"
|
|
111
|
+
else
|
|
112
|
+
echo " PASS: HTTP $status"
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Assert content signal
|
|
116
|
+
if [ -n "$content_signal" ]; then
|
|
117
|
+
if echo "$body" | grep -qiE "$content_signal"; then
|
|
118
|
+
echo " PASS: body contains \"$content_signal\""
|
|
119
|
+
else
|
|
120
|
+
echo " FAIL: body does not contain \"$content_signal\""
|
|
121
|
+
checks_failed=$((checks_failed + 1))
|
|
122
|
+
failures="${failures} - $name: missing content signal \"$content_signal\"\n"
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Assert response time
|
|
127
|
+
if [ -n "$max_response_time_ms" ] && [ "$response_time" -gt "$max_response_time_ms" ]; then
|
|
128
|
+
echo " FAIL: response time ${response_time}ms exceeds ${max_response_time_ms}ms"
|
|
129
|
+
checks_failed=$((checks_failed + 1))
|
|
130
|
+
failures="${failures} - $name: response time ${response_time}ms (max ${max_response_time_ms}ms)\n"
|
|
131
|
+
fi
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 4. Generate report
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
total=$((checks_passed + checks_failed))
|
|
138
|
+
echo ""
|
|
139
|
+
echo "=== Smoke Test Summary ==="
|
|
140
|
+
echo "Total: $total | Passed: $checks_passed | Failed: $checks_failed"
|
|
141
|
+
|
|
142
|
+
if [ "$checks_failed" -gt 0 ]; then
|
|
143
|
+
echo ""
|
|
144
|
+
echo "Failures:"
|
|
145
|
+
echo -e "$failures"
|
|
146
|
+
exit 1
|
|
147
|
+
else
|
|
148
|
+
echo "All checks passed."
|
|
149
|
+
exit 0
|
|
150
|
+
fi
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Runner script
|
|
154
|
+
|
|
155
|
+
A ready-to-use runner is provided for standalone operation:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
bash scripts/run-smoke.sh [url] [smoke-checks-file]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The runner:
|
|
162
|
+
1. Uses `$DEPLOY_URL`, `$SMOKE_CHECKS_FILE`, or CLI arguments
|
|
163
|
+
2. Runs all defined checks
|
|
164
|
+
3. Prints a pass/fail summary
|
|
165
|
+
4. Exits 0 on all pass, non-zero on any failure
|
|
166
|
+
|
|
167
|
+
## Integration with deploy skill
|
|
168
|
+
|
|
169
|
+
The `deploy` skill references `smoke-test` as its final verification step:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# In deploy workflow — after successful deploy
|
|
173
|
+
DEPLOY_URL="$DEPLOY_URL" bash scripts/run-smoke.sh
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Configuration reference
|
|
177
|
+
|
|
178
|
+
| Variable | Default | Description |
|
|
179
|
+
|----------|---------|-------------|
|
|
180
|
+
| `SMOKE_CHECKS_FILE` | `smoke-checks.yaml` | Path to smoke checks YAML |
|
|
181
|
+
| `DEPLOY_URL` / `BASE_URL` | *(required)* | Base URL for all checks |
|
|
182
|
+
| `SMOKE_TIMEOUT` | `30` | Per-check timeout (seconds) |
|
|
183
|
+
| `SMOKE_RETRIES` | `0` | Number of retries on failure |
|
|
184
|
+
|
|
185
|
+
## Verification
|
|
186
|
+
|
|
187
|
+
→ verify: `test -f smoke-test/SKILL.md && grep -q 'name: smoke-test' smoke-test/SKILL.md && echo OK`
|
|
188
|
+
→ verify: `grep -qi 'smoke.checks.yaml\|checklist\|expected_status\|content_signal' smoke-test/SKILL.md && echo OK`
|
|
189
|
+
→ verify: `grep -ci 'pass\|fail\|summary\|report' smoke-test/SKILL.md | awk '{if($1>=2) print "OK"; else print "FAIL"}'`
|
|
190
|
+
→ verify: `grep -q 'smoke-test' SKILL-INDEX.md && echo OK`
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: smoke-test
|
|
3
|
+
description: "\"Post-deploy health-check against a live URL. Validates HTTP status, response content, and critical endpoints. Runnable standalone OR as the final step of the deploy skill.\""
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Smoke Test
|
|
9
|
+
|
|
10
|
+
> **HARD GATE** — Do NOT run smoke-test against a URL that hasn't been deployed yet. Always run `deploy` first, then `smoke-test`.
|
|
11
|
+
>
|
|
12
|
+
> **HARD GATE** — A failed smoke test means the deployment is broken. Do NOT mark a deploy as successful until all smoke checks pass.
|
|
13
|
+
|
|
14
|
+
Validate a deployed application is healthy by running a configurable set of HTTP checks against live URLs. Each check asserts:
|
|
15
|
+
- HTTP status code (e.g., 200 for success, 404 for expected-not-found)
|
|
16
|
+
- Response body content signal (regex or jq expression)
|
|
17
|
+
- Response time threshold (optional)
|
|
18
|
+
|
|
19
|
+
Can be run standalone for quick health checks or chained as the final step of the `deploy` skill.
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
Smoke checks are defined in `smoke-checks.yaml` at the project root:
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
# smoke-checks.yaml — auto-loaded if present at project root
|
|
27
|
+
base_url: "https://example.com"
|
|
28
|
+
checks:
|
|
29
|
+
- name: "Homepage"
|
|
30
|
+
path: "/"
|
|
31
|
+
method: GET
|
|
32
|
+
expected_status: 200
|
|
33
|
+
content_signal: "bigpowers"
|
|
34
|
+
max_response_time_ms: 3000
|
|
35
|
+
|
|
36
|
+
- name: "API Health"
|
|
37
|
+
path: "/api/health"
|
|
38
|
+
method: GET
|
|
39
|
+
expected_status: 200
|
|
40
|
+
content_signal: "ok|healthy"
|
|
41
|
+
|
|
42
|
+
- name: "API Jogos"
|
|
43
|
+
path: "/api/jogos"
|
|
44
|
+
method: GET
|
|
45
|
+
expected_status: 200
|
|
46
|
+
content_signal: "jogos|games"
|
|
47
|
+
|
|
48
|
+
- name: "Not Found handling"
|
|
49
|
+
path: "/nonexistent"
|
|
50
|
+
method: GET
|
|
51
|
+
expected_status: 404
|
|
52
|
+
content_signal: "not found|404"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Checks can also be specified inline via environment variables or CLI arguments for ad-hoc use.
|
|
56
|
+
|
|
57
|
+
### Check Schema
|
|
58
|
+
|
|
59
|
+
| Field | Required | Default | Description |
|
|
60
|
+
|-------|----------|---------|-------------|
|
|
61
|
+
| `name` | Yes | — | Human-readable check name (used in report) |
|
|
62
|
+
| `path` | Yes | `/` | URL path relative to base_url |
|
|
63
|
+
| `method` | No | `GET` | HTTP method |
|
|
64
|
+
| `expected_status` | No | `200` | Expected HTTP status code |
|
|
65
|
+
| `content_signal` | No | — | Regex or string to find in response body |
|
|
66
|
+
| `max_response_time_ms` | No | — | Fail if response slower than this threshold (ms) |
|
|
67
|
+
|
|
68
|
+
## Process
|
|
69
|
+
|
|
70
|
+
### 1. Load smoke checks
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
SMOKE_CHECKS_FILE="${SMOKE_CHECKS_FILE:-smoke-checks.yaml}"
|
|
74
|
+
BASE_URL="${DEPLOY_URL:-$BASE_URL}"
|
|
75
|
+
|
|
76
|
+
if [ -f "$SMOKE_CHECKS_FILE" ]; then
|
|
77
|
+
echo "Loaded smoke checks from $SMOKE_CHECKS_FILE"
|
|
78
|
+
elif [ -n "$BASE_URL" ]; then
|
|
79
|
+
echo "No smoke-checks.yaml found. Using single URL check against $BASE_URL"
|
|
80
|
+
else
|
|
81
|
+
echo "ERROR: No smoke-checks.yaml found and no DEPLOY_URL/BASE_URL set."
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Run each check
|
|
87
|
+
|
|
88
|
+
For each check in the configuration, perform an HTTP request:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
url="${BASE_URL}${path}"
|
|
92
|
+
start_time=$(python3 -c 'import time; print(int(time.time() * 1000))')
|
|
93
|
+
|
|
94
|
+
# Perform the HTTP request
|
|
95
|
+
response=$(curl -s -o /tmp/smoke_body.txt -w "%{http_code}" "$url")
|
|
96
|
+
response_time=$(( $(python3 -c 'import time; print(int(time.time() * 1000))') - start_time ))
|
|
97
|
+
status=$response
|
|
98
|
+
body=$(cat /tmp/smoke_body.txt)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. Assert results
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
checks_passed=0
|
|
105
|
+
checks_failed=0
|
|
106
|
+
failures=""
|
|
107
|
+
|
|
108
|
+
# Assert status code
|
|
109
|
+
if [ "$status" -ne "${expected_status:-200}" ]; then
|
|
110
|
+
echo " FAIL: expected status ${expected_status} but got $status"
|
|
111
|
+
checks_failed=$((checks_failed + 1))
|
|
112
|
+
failures="${failures} - $name: HTTP $status (expected ${expected_status})\n"
|
|
113
|
+
else
|
|
114
|
+
echo " PASS: HTTP $status"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# Assert content signal
|
|
118
|
+
if [ -n "$content_signal" ]; then
|
|
119
|
+
if echo "$body" | grep -qiE "$content_signal"; then
|
|
120
|
+
echo " PASS: body contains \"$content_signal\""
|
|
121
|
+
else
|
|
122
|
+
echo " FAIL: body does not contain \"$content_signal\""
|
|
123
|
+
checks_failed=$((checks_failed + 1))
|
|
124
|
+
failures="${failures} - $name: missing content signal \"$content_signal\"\n"
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Assert response time
|
|
129
|
+
if [ -n "$max_response_time_ms" ] && [ "$response_time" -gt "$max_response_time_ms" ]; then
|
|
130
|
+
echo " FAIL: response time ${response_time}ms exceeds ${max_response_time_ms}ms"
|
|
131
|
+
checks_failed=$((checks_failed + 1))
|
|
132
|
+
failures="${failures} - $name: response time ${response_time}ms (max ${max_response_time_ms}ms)\n"
|
|
133
|
+
fi
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 4. Generate report
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
total=$((checks_passed + checks_failed))
|
|
140
|
+
echo ""
|
|
141
|
+
echo "=== Smoke Test Summary ==="
|
|
142
|
+
echo "Total: $total | Passed: $checks_passed | Failed: $checks_failed"
|
|
143
|
+
|
|
144
|
+
if [ "$checks_failed" -gt 0 ]; then
|
|
145
|
+
echo ""
|
|
146
|
+
echo "Failures:"
|
|
147
|
+
echo -e "$failures"
|
|
148
|
+
exit 1
|
|
149
|
+
else
|
|
150
|
+
echo "All checks passed."
|
|
151
|
+
exit 0
|
|
152
|
+
fi
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Runner script
|
|
156
|
+
|
|
157
|
+
A ready-to-use runner is provided for standalone operation:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
bash scripts/run-smoke.sh [url] [smoke-checks-file]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The runner:
|
|
164
|
+
1. Uses `$DEPLOY_URL`, `$SMOKE_CHECKS_FILE`, or CLI arguments
|
|
165
|
+
2. Runs all defined checks
|
|
166
|
+
3. Prints a pass/fail summary
|
|
167
|
+
4. Exits 0 on all pass, non-zero on any failure
|
|
168
|
+
|
|
169
|
+
## Integration with deploy skill
|
|
170
|
+
|
|
171
|
+
The `deploy` skill references `smoke-test` as its final verification step:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# In deploy workflow — after successful deploy
|
|
175
|
+
DEPLOY_URL="$DEPLOY_URL" bash scripts/run-smoke.sh
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Configuration reference
|
|
179
|
+
|
|
180
|
+
| Variable | Default | Description |
|
|
181
|
+
|----------|---------|-------------|
|
|
182
|
+
| `SMOKE_CHECKS_FILE` | `smoke-checks.yaml` | Path to smoke checks YAML |
|
|
183
|
+
| `DEPLOY_URL` / `BASE_URL` | *(required)* | Base URL for all checks |
|
|
184
|
+
| `SMOKE_TIMEOUT` | `30` | Per-check timeout (seconds) |
|
|
185
|
+
| `SMOKE_RETRIES` | `0` | Number of retries on failure |
|
|
186
|
+
|
|
187
|
+
## Verification
|
|
188
|
+
|
|
189
|
+
→ verify: `test -f smoke-test/SKILL.md && grep -q 'name: smoke-test' smoke-test/SKILL.md && echo OK`
|
|
190
|
+
→ verify: `grep -qi 'smoke.checks.yaml\|checklist\|expected_status\|content_signal' smoke-test/SKILL.md && echo OK`
|
|
191
|
+
→ verify: `grep -ci 'pass\|fail\|summary\|report' smoke-test/SKILL.md | awk '{if($1>=2) print "OK"; else print "FAIL"}'`
|
|
192
|
+
→ verify: `grep -q 'smoke-test' SKILL-INDEX.md && echo OK`
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [2.12.1](https://github.com/danielvm-git/bigpowers/compare/v2.12.0...v2.12.1) (2026-06-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **skills:** add run-smoke.sh referenced by deploy, smoke-test ([5108891](https://github.com/danielvm-git/bigpowers/commit/51088912084979df38d6bb529297b03a34a6bb66))
|
|
7
|
+
|
|
8
|
+
# [2.12.0](https://github.com/danielvm-git/bigpowers/compare/v2.11.0...v2.12.0) (2026-06-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **skills:** add smoke-test skill — post-deploy health-check validation ([f34cd76](https://github.com/danielvm-git/bigpowers/commit/f34cd765ffc267bc332b9cf2c6aca6431b004c50))
|
|
14
|
+
|
|
1
15
|
# [2.11.0](https://github.com/danielvm-git/bigpowers/compare/v2.10.0...v2.11.0) (2026-06-20)
|
|
2
16
|
|
|
3
17
|
|
package/SKILL-INDEX.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
> **DO NOT EDIT** — This file is auto-generated by `scripts/generate-skill-index.sh`.
|
|
4
4
|
> Edit `SKILL.md` source files or `skills-lock.json` instead. Run `bash scripts/sync-skills.sh` to regenerate.
|
|
5
5
|
|
|
6
|
-
**Generated:** 2026-06-20T21:
|
|
7
|
-
**Skills:**
|
|
6
|
+
**Generated:** 2026-06-20T21:59:15Z
|
|
7
|
+
**Skills:** 66
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
| Discover | 6 | `elaborate-spec, map-codebase, research-first, search-skills, survey-context, using-bigpowers` |
|
|
16
16
|
| Design | 7 | `deepen-architecture, define-language, define-success, design-interface, grill-me, grill-with-docs, model-domain` |
|
|
17
17
|
| Plan | 9 | `assess-impact, change-request, plan-refactor, plan-release, plan-work, run-planning, scope-work, seed-conventions, slice-tasks` |
|
|
18
|
-
| Build |
|
|
18
|
+
| Build | 16 | `align-grid, build-epic, craft-skill, deploy, develop-tdd, execute-plan, guard-git, hook-commits, kickoff-branch, orchestrate-project, publish-package, setup-environment, smoke-test, spike-prototype, wire-ci, wire-observability` |
|
|
19
19
|
| Verify | 12 | `audit-code, diagnose-root, enforce-first, fix-bug, inspect-quality, investigate-bug, request-review, respond-review, run-evals, trace-requirement, validate-fix, verify-work` |
|
|
20
20
|
| Release | 2 | `commit-message, release-branch` |
|
|
21
21
|
| Sustain | 13 | `compose-workflow, delegate-task, dispatch-agents, edit-document, evolve-skill, migrate-spec, organize-workspace, reset-baseline, session-state, simulate-agents, stocktake-skills, terse-mode, write-document` |
|
|
22
|
-
| **TOTAL** | **
|
|
22
|
+
| **TOTAL** | **65** | |
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -61,38 +61,39 @@
|
|
|
61
61
|
| 32 | Build | `orchestrate-project` | Meta-skill that enforces the 6-phase core loop (discover → elaborate → plan | ✅ Active |
|
|
62
62
|
| 33 | Build | `publish-package` | "Package registry publishing for npm, crates.io, PyPI, and Homebrew. Verifies pr | ✅ Active |
|
|
63
63
|
| 34 | Build | `setup-environment` | Pre-install dependencies and configure tools before development work begins. Use | ✅ Active |
|
|
64
|
-
| 35 | Build | `
|
|
65
|
-
| 36 | Build | `
|
|
66
|
-
| 37 | Build | `wire-
|
|
67
|
-
| 38 |
|
|
68
|
-
| 39 | Verify | `
|
|
69
|
-
| 40 | Verify | `
|
|
70
|
-
| 41 | Verify | `
|
|
71
|
-
| 42 | Verify | `
|
|
72
|
-
| 43 | Verify | `
|
|
73
|
-
| 44 | Verify | `
|
|
74
|
-
| 45 | Verify | `
|
|
75
|
-
| 46 | Verify | `
|
|
76
|
-
| 47 | Verify | `
|
|
77
|
-
| 48 | Verify | `
|
|
78
|
-
| 49 | Verify | `
|
|
79
|
-
| 50 |
|
|
80
|
-
| 51 | Release | `
|
|
81
|
-
| 52 |
|
|
82
|
-
| 53 | Sustain | `
|
|
83
|
-
| 54 | Sustain | `
|
|
84
|
-
| 55 | Sustain | `
|
|
85
|
-
| 56 | Sustain | `
|
|
86
|
-
| 57 | Sustain | `
|
|
87
|
-
| 58 | Sustain | `
|
|
88
|
-
| 59 | Sustain | `
|
|
89
|
-
| 60 | Sustain | `
|
|
90
|
-
| 61 | Sustain | `
|
|
91
|
-
| 62 | Sustain | `
|
|
92
|
-
| 63 | Sustain | `
|
|
93
|
-
| 64 | Sustain | `
|
|
94
|
-
|
|
95
|
-
|
|
64
|
+
| 35 | Build | `smoke-test` | "Post-deploy health-check against a live URL. Validates HTTP status, response co | ✅ Active |
|
|
65
|
+
| 36 | Build | `spike-prototype` | Throw-away prototype for unknown problem spaces. Output is learning notes in spe | ✅ Active |
|
|
66
|
+
| 37 | Build | `wire-ci` | "CI pipeline setup with pre-built templates and local validation. Generates GitH | ✅ Active |
|
|
67
|
+
| 38 | Build | `wire-observability` | Add structured JSON logging, observability commands, and idempotent setup script | ✅ Active |
|
|
68
|
+
| 39 | Verify | `audit-code` | Self-review checklist for the coding agent to run before dispatching a reviewer. | ✅ Active |
|
|
69
|
+
| 40 | Verify | `diagnose-root` | Run 4-phase root cause analysis — reproduce, isolate, hypothesize, verify. Use | ✅ Active |
|
|
70
|
+
| 41 | Verify | `enforce-first` | Apply the F.I.R.S.T test quality rubric (Fast, Independent, Repeatable, Self-Val | ✅ Active |
|
|
71
|
+
| 42 | Verify | `fix-bug` | Bug fix orchestrator — active_flow fix_bug; reads specs/bugs/BUG-*.md; chains | ✅ Active |
|
|
72
|
+
| 43 | Verify | `inspect-quality` | Interactive QA session where user reports bugs or issues conversationally, and t | ✅ Active |
|
|
73
|
+
| 44 | Verify | `investigate-bug` | Investigate a bug or issue by exploring the codebase to find root cause, then wr | ✅ Active |
|
|
74
|
+
| 45 | Verify | `request-review` | Dispatch a fresh reviewer agent with a clean context to critique the code after | ✅ Active |
|
|
75
|
+
| 46 | Verify | `respond-review` | Act on a reviewer agent's feedback systematically — categorize findings, apply | ✅ Active |
|
|
76
|
+
| 47 | Verify | `run-evals` | Eval-Driven Development — define capability and regression evals before buildi | ✅ Active |
|
|
77
|
+
| 48 | Verify | `trace-requirement` | Link story IDs from specs/release-plan.yaml + epic capsule directories to the im | ✅ Active |
|
|
78
|
+
| 49 | Verify | `validate-fix` | Prove a fix works before declaring done — re-run the failing test, run the ful | ✅ Active |
|
|
79
|
+
| 50 | Verify | `verify-work` | Multi-phase UAT gate — cold-start smoke, build, typecheck, lint, tests, step-b | ✅ Active |
|
|
80
|
+
| 51 | Release | `commit-message` | Reviews working-tree changes, then drafts a Conventional Commits title/body and | ✅ Active |
|
|
81
|
+
| 52 | Release | `release-branch` | Make the merge/PR/keep/discard decision for a feature branch, verify coverage ga | ✅ Active |
|
|
82
|
+
| 53 | Sustain | `compose-workflow` | Chain multiple bigpowers skills into a custom workflow recipe saved in specs/. U | ✅ Active |
|
|
83
|
+
| 54 | Sustain | `delegate-task` | Delegate one complex task to a single subagent, review its work in two stages be | ✅ Active |
|
|
84
|
+
| 55 | Sustain | `dispatch-agents` | Dispatch multiple subagents in parallel on independent tasks. No waiting between | ✅ Active |
|
|
85
|
+
| 56 | Sustain | `edit-document` | Edit and improve documents by restructuring sections, improving clarity, and tig | ✅ Active |
|
|
86
|
+
| 57 | Sustain | `evolve-skill` | Benchmark-gated skill evolution — consume bigpowers-benchmark report, propose | ✅ Active |
|
|
87
|
+
| 58 | Sustain | `migrate-spec` | Detect GSD, spec-kit, or BMAD spec artifacts and transform them into bigpowers Y | ✅ Active |
|
|
88
|
+
| 59 | Sustain | `organize-workspace` | Scans the active workspace for disposable artifacts—logs, caches, stale build | ✅ Active |
|
|
89
|
+
| 60 | Sustain | `reset-baseline` | Restore the project to a known clean state between agent runs or experiments. Us | ✅ Active |
|
|
90
|
+
| 61 | Sustain | `session-state` | Track implementation decisions and progress in specs/state.yaml to prevent conte | ✅ Active |
|
|
91
|
+
| 62 | Sustain | `simulate-agents` | Run Mock User and Auditor agents against a feature in fresh contexts before huma | ✅ Active |
|
|
92
|
+
| 63 | Sustain | `stocktake-skills` | Sequential subagent batch audit of the bigpowers skill catalog — Quick Scan (c | ✅ Active |
|
|
93
|
+
| 64 | Sustain | `terse-mode` | Fallback ultra-compressed communication mode. Cuts token usage ~75% by dropping | ✅ Active |
|
|
94
|
+
| 65 | Sustain | `write-document` | Write, organize, and sync high-integrity technical documents using the BMAD meth | ✅ Active |
|
|
95
|
+
|
|
96
|
+
**Total: 65 active skills.**
|
|
96
97
|
|
|
97
98
|
---
|
|
98
99
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# run-smoke.sh — Post-deploy health-check runner for smoke-test skill
|
|
3
|
+
# Usage: bash scripts/run-smoke.sh [url] [smoke-checks-file]
|
|
4
|
+
#
|
|
5
|
+
# Reads from smoke-checks.yaml by default. Falls back to single-URL check.
|
|
6
|
+
# Env vars: DEPLOY_URL, SMOKE_CHECKS_FILE, SMOKE_TIMEOUT, SMOKE_RETRIES
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
SMOKE_CHECKS_FILE="${2:-${SMOKE_CHECKS_FILE:-smoke-checks.yaml}}"
|
|
10
|
+
BASE_URL="${1:-${DEPLOY_URL:-}}"
|
|
11
|
+
SMOKE_TIMEOUT="${SMOKE_TIMEOUT:-30}"
|
|
12
|
+
SMOKE_RETRIES="${SMOKE_RETRIES:-0}"
|
|
13
|
+
|
|
14
|
+
TMPDIR=$(mktemp -d)
|
|
15
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
16
|
+
|
|
17
|
+
checks_passed=0
|
|
18
|
+
checks_failed=0
|
|
19
|
+
failures=""
|
|
20
|
+
|
|
21
|
+
# ── 1. Load checks ──────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
run_single_url_check() {
|
|
24
|
+
local url="$1"
|
|
25
|
+
local name="${2:-URL check}"
|
|
26
|
+
local expected_status="${3:-200}"
|
|
27
|
+
local content_signal="${4:-}"
|
|
28
|
+
|
|
29
|
+
echo "[$name]"
|
|
30
|
+
start_time=$(python3 -c 'import time; print(int(time.time() * 1000))')
|
|
31
|
+
response=$(curl -s -o "$TMPDIR/body.txt" -w "%{http_code}" --max-time "$SMOKE_TIMEOUT" "$url")
|
|
32
|
+
response_time=$(( $(python3 -c 'import time; print(int(time.time() * 1000))') - start_time ))
|
|
33
|
+
status="$response"
|
|
34
|
+
body=$(cat "$TMPDIR/body.txt" 2>/dev/null || echo "")
|
|
35
|
+
|
|
36
|
+
# Assert status
|
|
37
|
+
if [ "$status" -ne "$expected_status" ]; then
|
|
38
|
+
echo " FAIL: HTTP $status (expected $expected_status)"
|
|
39
|
+
checks_failed=$((checks_failed + 1))
|
|
40
|
+
failures="${failures} - $name: HTTP $status (expected $expected_status)\n"
|
|
41
|
+
else
|
|
42
|
+
echo " PASS: HTTP $status"
|
|
43
|
+
checks_passed=$((checks_passed + 1))
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Assert content signal
|
|
47
|
+
if [ -n "$content_signal" ]; then
|
|
48
|
+
if echo "$body" | grep -qiE "$content_signal"; then
|
|
49
|
+
echo " PASS: body matches \"$content_signal\""
|
|
50
|
+
else
|
|
51
|
+
echo " FAIL: body does not match \"$content_signal\""
|
|
52
|
+
checks_failed=$((checks_failed + 1))
|
|
53
|
+
failures="${failures} - $name: missing content signal \"$content_signal\"\n"
|
|
54
|
+
fi
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Assert response time
|
|
58
|
+
if [ "$response_time" -gt 0 ]; then
|
|
59
|
+
echo " Time: ${response_time}ms"
|
|
60
|
+
fi
|
|
61
|
+
echo ""
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
parse_and_run_checks() {
|
|
65
|
+
local file="$1"
|
|
66
|
+
local base_url=""
|
|
67
|
+
local in_checks=false
|
|
68
|
+
local check_name="" check_path="" check_method="" check_status="" check_signal="" check_time=""
|
|
69
|
+
|
|
70
|
+
while IFS= read -r line; do
|
|
71
|
+
# Strip inline comments
|
|
72
|
+
line="${line%%#*}"
|
|
73
|
+
# Trim whitespace
|
|
74
|
+
line="${line#"${line%%[![:space:]]*}"}"
|
|
75
|
+
line="${line%"${line##*[![:space:]]}"}"
|
|
76
|
+
[ -z "$line" ] && continue
|
|
77
|
+
|
|
78
|
+
if [[ "$line" =~ ^base_url: ]]; then
|
|
79
|
+
base_url="${line#base_url:}"
|
|
80
|
+
base_url="${base_url#"${base_url%%[![:space:]]*}"}"
|
|
81
|
+
base_url="${base_url%"${base_url##*[![:space:]]}"}"
|
|
82
|
+
base_url="${base_url%\"}"
|
|
83
|
+
base_url="${base_url#\"}"
|
|
84
|
+
base_url="${base_url%\'}"
|
|
85
|
+
base_url="${base_url#\'}"
|
|
86
|
+
continue
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [[ "$line" == "checks:" ]]; then
|
|
90
|
+
in_checks=true
|
|
91
|
+
check_name=""; check_path=""; check_method="GET"; check_status="200"
|
|
92
|
+
check_signal=""; check_time=""
|
|
93
|
+
continue
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if $in_checks; then
|
|
97
|
+
# Detect new check entry (dash at start of list item)
|
|
98
|
+
if [[ "$line" == "- name:"* ]]; then
|
|
99
|
+
# Run previous check if accumulated
|
|
100
|
+
if [ -n "$check_name" ] && [ -n "$base_url" ]; then
|
|
101
|
+
run_parsed_check "$base_url" "$check_name" "$check_path" "$check_status" "$check_signal"
|
|
102
|
+
fi
|
|
103
|
+
check_name="${line#- name:}"
|
|
104
|
+
check_name="${check_name#"${check_name%%[![:space:]]*}"}"
|
|
105
|
+
check_name="${check_name%\"}"; check_name="${check_name#\"}"
|
|
106
|
+
check_name="${check_name%\'}"; check_name="${check_name#\'}"
|
|
107
|
+
check_path=""; check_method="GET"; check_status="200"; check_signal=""; check_time=""
|
|
108
|
+
elif [[ "$line" == "name:"* ]]; then
|
|
109
|
+
check_name="${line#name:}"
|
|
110
|
+
check_name="${check_name#"${check_name%%[![:space:]]*}"}"
|
|
111
|
+
check_name="${check_name%\"}"; check_name="${check_name#\"}"
|
|
112
|
+
check_name="${check_name%\'}"; check_name="${check_name#\'}"
|
|
113
|
+
elif [[ "$line" == "path:"* ]]; then
|
|
114
|
+
check_path="${line#path:}"
|
|
115
|
+
check_path="${check_path#"${check_path%%[![:space:]]*}"}"
|
|
116
|
+
check_path="${check_path%\"}"; check_path="${check_path#\"}"
|
|
117
|
+
check_path="${check_path%\'}"; check_path="${check_path#\'}"
|
|
118
|
+
elif [[ "$line" == "method:"* ]]; then
|
|
119
|
+
check_method="${line#method:}"
|
|
120
|
+
check_method="${check_method#"${check_method%%[![:space:]]*}"}"
|
|
121
|
+
check_method="${check_method%\"}"; check_method="${check_method#\"}"
|
|
122
|
+
check_method="${check_method%\'}"; check_method="${check_method#\'}"
|
|
123
|
+
elif [[ "$line" == "expected_status:"* ]]; then
|
|
124
|
+
check_status="${line#expected_status:}"
|
|
125
|
+
check_status="${check_status#"${check_status%%[![:space:]]*}"}"
|
|
126
|
+
elif [[ "$line" == "content_signal:"* ]]; then
|
|
127
|
+
check_signal="${line#content_signal:}"
|
|
128
|
+
check_signal="${check_signal#"${check_signal%%[![:space:]]*}"}"
|
|
129
|
+
check_signal="${check_signal%\"}"; check_signal="${check_signal#\"}"
|
|
130
|
+
check_signal="${check_signal%\'}"; check_signal="${check_signal#\'}"
|
|
131
|
+
elif [[ "$line" == "max_response_time_ms:"* ]]; then
|
|
132
|
+
check_time="${line#max_response_time_ms:}"
|
|
133
|
+
check_time="${check_time#"${check_time%%[![:space:]]*}"}"
|
|
134
|
+
fi
|
|
135
|
+
fi
|
|
136
|
+
done < "$file"
|
|
137
|
+
|
|
138
|
+
# Run last check
|
|
139
|
+
if [ -n "$check_name" ] && [ -n "$base_url" ]; then
|
|
140
|
+
run_parsed_check "$base_url" "$check_name" "$check_path" "$check_status" "$check_signal"
|
|
141
|
+
fi
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
run_parsed_check() {
|
|
145
|
+
local base_url="$1" name="$2" path="$3" expected_status="$4" content_signal="$5"
|
|
146
|
+
local url="${base_url}${path}"
|
|
147
|
+
|
|
148
|
+
echo "[$name]"
|
|
149
|
+
echo " URL: $url"
|
|
150
|
+
start_time=$(python3 -c 'import time; print(int(time.time() * 1000))')
|
|
151
|
+
response=$(curl -s -o "$TMPDIR/body.txt" -w "%{http_code}" --max-time "$SMOKE_TIMEOUT" "$url" 2>/dev/null || echo "000")
|
|
152
|
+
response_time=$(( $(python3 -c 'import time; print(int(time.time() * 1000))') - start_time ))
|
|
153
|
+
status="$response"
|
|
154
|
+
body=$(cat "$TMPDIR/body.txt" 2>/dev/null || echo "")
|
|
155
|
+
|
|
156
|
+
# Assert status code
|
|
157
|
+
if [ "$status" -ne "$expected_status" ]; then
|
|
158
|
+
echo " FAIL: HTTP $status (expected $expected_status)"
|
|
159
|
+
checks_failed=$((checks_failed + 1))
|
|
160
|
+
failures="${failures} - $name: HTTP $status (expected $expected_status)\n"
|
|
161
|
+
else
|
|
162
|
+
echo " PASS: HTTP $status"
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# Assert content signal
|
|
166
|
+
if [ -n "$content_signal" ]; then
|
|
167
|
+
if echo "$body" | grep -qiE "$content_signal"; then
|
|
168
|
+
echo " PASS: body matches \"$content_signal\""
|
|
169
|
+
checks_passed=$((checks_passed + 1))
|
|
170
|
+
else
|
|
171
|
+
echo " FAIL: body does not match \"$content_signal\""
|
|
172
|
+
checks_failed=$((checks_failed + 1))
|
|
173
|
+
failures="${failures} - $name: missing content signal \"$content_signal\"\n"
|
|
174
|
+
fi
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# Assert response time
|
|
178
|
+
if [ -n "${check_time:-}" ] && [ "$check_time" -gt 0 ] 2>/dev/null; then
|
|
179
|
+
if [ "$response_time" -gt "$check_time" ]; then
|
|
180
|
+
echo " FAIL: ${response_time}ms exceeds ${check_time}ms max"
|
|
181
|
+
checks_failed=$((checks_failed + 1))
|
|
182
|
+
failures="${failures} - $name: response time ${response_time}ms (max ${check_time}ms)\n"
|
|
183
|
+
else
|
|
184
|
+
echo " PASS: ${response_time}ms within ${check_time}ms limit"
|
|
185
|
+
fi
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
if [ "$response_time" -gt 0 ]; then
|
|
189
|
+
echo " Time: ${response_time}ms"
|
|
190
|
+
fi
|
|
191
|
+
echo ""
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# ── Main dispatch ───────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
echo "=== Smoke Test Runner ==="
|
|
197
|
+
echo "Started at: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
|
198
|
+
echo ""
|
|
199
|
+
|
|
200
|
+
if [ -f "$SMOKE_CHECKS_FILE" ]; then
|
|
201
|
+
echo "Checks file: $SMOKE_CHECKS_FILE"
|
|
202
|
+
parse_and_run_checks "$SMOKE_CHECKS_FILE"
|
|
203
|
+
elif [ -n "$BASE_URL" ]; then
|
|
204
|
+
echo "Single URL mode: $BASE_URL"
|
|
205
|
+
run_single_url_check "$BASE_URL" "Baseline"
|
|
206
|
+
else
|
|
207
|
+
echo "ERROR: No smoke-checks.yaml found and no URL provided."
|
|
208
|
+
echo "Usage: bash scripts/run-smoke.sh [url] [smoke-checks-file]"
|
|
209
|
+
echo " DEPLOY_URL=http://example.com bash scripts/run-smoke.sh"
|
|
210
|
+
exit 2
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# ── Summary ──────────────────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
total=$((checks_passed + checks_failed))
|
|
216
|
+
echo "=== Smoke Test Summary ==="
|
|
217
|
+
echo "Total: $total | Passed: $checks_passed | Failed: $checks_failed"
|
|
218
|
+
echo ""
|
|
219
|
+
|
|
220
|
+
if [ "$checks_failed" -gt 0 ]; then
|
|
221
|
+
echo "Failures:"
|
|
222
|
+
echo -e "$failures"
|
|
223
|
+
echo "Smoke test FAILED."
|
|
224
|
+
exit 1
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
if [ "$total" -eq 0 ]; then
|
|
228
|
+
echo "No checks were executed."
|
|
229
|
+
exit 1
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
echo "All checks passed."
|
|
233
|
+
exit 0
|
package/skills-lock.json
CHANGED
|
@@ -266,6 +266,11 @@
|
|
|
266
266
|
"sha256": "bda9db54dbe791b5",
|
|
267
267
|
"path": "slice-tasks/SKILL.md"
|
|
268
268
|
},
|
|
269
|
+
"smoke-test": {
|
|
270
|
+
"description": "\"Post-deploy health-check against a live URL. Validates HTTP status, response content, and critical endpoints. Runnable standalone OR as the final step of the deploy skill.\"",
|
|
271
|
+
"sha256": "402c89c31ac4fd4c",
|
|
272
|
+
"path": "smoke-test/SKILL.md"
|
|
273
|
+
},
|
|
269
274
|
"spike-prototype": {
|
|
270
275
|
"description": "Throw-away prototype for unknown problem spaces. Output is learning notes in specs/archive/spikes/SPIKE-<name>.md, not production code. Use when the domain or technology is unexplored, when estimates are impossible without experimentation, or when user says \"spike\", \"prototype\", or \"proof of concept\".",
|
|
271
276
|
"sha256": "568e52ae1e3a9213",
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: smoke-test
|
|
3
|
+
description: "Post-deploy health-check against a live URL. Validates HTTP status, response content, and critical endpoints. Runnable standalone OR as the final step of the deploy skill."
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Smoke Test
|
|
8
|
+
|
|
9
|
+
> **HARD GATE** — Do NOT run smoke-test against a URL that hasn't been deployed yet. Always run `deploy` first, then `smoke-test`.
|
|
10
|
+
>
|
|
11
|
+
> **HARD GATE** — A failed smoke test means the deployment is broken. Do NOT mark a deploy as successful until all smoke checks pass.
|
|
12
|
+
|
|
13
|
+
Validate a deployed application is healthy by running a configurable set of HTTP checks against live URLs. Each check asserts:
|
|
14
|
+
- HTTP status code (e.g., 200 for success, 404 for expected-not-found)
|
|
15
|
+
- Response body content signal (regex or jq expression)
|
|
16
|
+
- Response time threshold (optional)
|
|
17
|
+
|
|
18
|
+
Can be run standalone for quick health checks or chained as the final step of the `deploy` skill.
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
Smoke checks are defined in `smoke-checks.yaml` at the project root:
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
# smoke-checks.yaml — auto-loaded if present at project root
|
|
26
|
+
base_url: "https://example.com"
|
|
27
|
+
checks:
|
|
28
|
+
- name: "Homepage"
|
|
29
|
+
path: "/"
|
|
30
|
+
method: GET
|
|
31
|
+
expected_status: 200
|
|
32
|
+
content_signal: "bigpowers"
|
|
33
|
+
max_response_time_ms: 3000
|
|
34
|
+
|
|
35
|
+
- name: "API Health"
|
|
36
|
+
path: "/api/health"
|
|
37
|
+
method: GET
|
|
38
|
+
expected_status: 200
|
|
39
|
+
content_signal: "ok|healthy"
|
|
40
|
+
|
|
41
|
+
- name: "API Jogos"
|
|
42
|
+
path: "/api/jogos"
|
|
43
|
+
method: GET
|
|
44
|
+
expected_status: 200
|
|
45
|
+
content_signal: "jogos|games"
|
|
46
|
+
|
|
47
|
+
- name: "Not Found handling"
|
|
48
|
+
path: "/nonexistent"
|
|
49
|
+
method: GET
|
|
50
|
+
expected_status: 404
|
|
51
|
+
content_signal: "not found|404"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Checks can also be specified inline via environment variables or CLI arguments for ad-hoc use.
|
|
55
|
+
|
|
56
|
+
### Check Schema
|
|
57
|
+
|
|
58
|
+
| Field | Required | Default | Description |
|
|
59
|
+
|-------|----------|---------|-------------|
|
|
60
|
+
| `name` | Yes | — | Human-readable check name (used in report) |
|
|
61
|
+
| `path` | Yes | `/` | URL path relative to base_url |
|
|
62
|
+
| `method` | No | `GET` | HTTP method |
|
|
63
|
+
| `expected_status` | No | `200` | Expected HTTP status code |
|
|
64
|
+
| `content_signal` | No | — | Regex or string to find in response body |
|
|
65
|
+
| `max_response_time_ms` | No | — | Fail if response slower than this threshold (ms) |
|
|
66
|
+
|
|
67
|
+
## Process
|
|
68
|
+
|
|
69
|
+
### 1. Load smoke checks
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
SMOKE_CHECKS_FILE="${SMOKE_CHECKS_FILE:-smoke-checks.yaml}"
|
|
73
|
+
BASE_URL="${DEPLOY_URL:-$BASE_URL}"
|
|
74
|
+
|
|
75
|
+
if [ -f "$SMOKE_CHECKS_FILE" ]; then
|
|
76
|
+
echo "Loaded smoke checks from $SMOKE_CHECKS_FILE"
|
|
77
|
+
elif [ -n "$BASE_URL" ]; then
|
|
78
|
+
echo "No smoke-checks.yaml found. Using single URL check against $BASE_URL"
|
|
79
|
+
else
|
|
80
|
+
echo "ERROR: No smoke-checks.yaml found and no DEPLOY_URL/BASE_URL set."
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 2. Run each check
|
|
86
|
+
|
|
87
|
+
For each check in the configuration, perform an HTTP request:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
url="${BASE_URL}${path}"
|
|
91
|
+
start_time=$(python3 -c 'import time; print(int(time.time() * 1000))')
|
|
92
|
+
|
|
93
|
+
# Perform the HTTP request
|
|
94
|
+
response=$(curl -s -o /tmp/smoke_body.txt -w "%{http_code}" "$url")
|
|
95
|
+
response_time=$(( $(python3 -c 'import time; print(int(time.time() * 1000))') - start_time ))
|
|
96
|
+
status=$response
|
|
97
|
+
body=$(cat /tmp/smoke_body.txt)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 3. Assert results
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
checks_passed=0
|
|
104
|
+
checks_failed=0
|
|
105
|
+
failures=""
|
|
106
|
+
|
|
107
|
+
# Assert status code
|
|
108
|
+
if [ "$status" -ne "${expected_status:-200}" ]; then
|
|
109
|
+
echo " FAIL: expected status ${expected_status} but got $status"
|
|
110
|
+
checks_failed=$((checks_failed + 1))
|
|
111
|
+
failures="${failures} - $name: HTTP $status (expected ${expected_status})\n"
|
|
112
|
+
else
|
|
113
|
+
echo " PASS: HTTP $status"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Assert content signal
|
|
117
|
+
if [ -n "$content_signal" ]; then
|
|
118
|
+
if echo "$body" | grep -qiE "$content_signal"; then
|
|
119
|
+
echo " PASS: body contains \"$content_signal\""
|
|
120
|
+
else
|
|
121
|
+
echo " FAIL: body does not contain \"$content_signal\""
|
|
122
|
+
checks_failed=$((checks_failed + 1))
|
|
123
|
+
failures="${failures} - $name: missing content signal \"$content_signal\"\n"
|
|
124
|
+
fi
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# Assert response time
|
|
128
|
+
if [ -n "$max_response_time_ms" ] && [ "$response_time" -gt "$max_response_time_ms" ]; then
|
|
129
|
+
echo " FAIL: response time ${response_time}ms exceeds ${max_response_time_ms}ms"
|
|
130
|
+
checks_failed=$((checks_failed + 1))
|
|
131
|
+
failures="${failures} - $name: response time ${response_time}ms (max ${max_response_time_ms}ms)\n"
|
|
132
|
+
fi
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 4. Generate report
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
total=$((checks_passed + checks_failed))
|
|
139
|
+
echo ""
|
|
140
|
+
echo "=== Smoke Test Summary ==="
|
|
141
|
+
echo "Total: $total | Passed: $checks_passed | Failed: $checks_failed"
|
|
142
|
+
|
|
143
|
+
if [ "$checks_failed" -gt 0 ]; then
|
|
144
|
+
echo ""
|
|
145
|
+
echo "Failures:"
|
|
146
|
+
echo -e "$failures"
|
|
147
|
+
exit 1
|
|
148
|
+
else
|
|
149
|
+
echo "All checks passed."
|
|
150
|
+
exit 0
|
|
151
|
+
fi
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Runner script
|
|
155
|
+
|
|
156
|
+
A ready-to-use runner is provided for standalone operation:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
bash scripts/run-smoke.sh [url] [smoke-checks-file]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The runner:
|
|
163
|
+
1. Uses `$DEPLOY_URL`, `$SMOKE_CHECKS_FILE`, or CLI arguments
|
|
164
|
+
2. Runs all defined checks
|
|
165
|
+
3. Prints a pass/fail summary
|
|
166
|
+
4. Exits 0 on all pass, non-zero on any failure
|
|
167
|
+
|
|
168
|
+
## Integration with deploy skill
|
|
169
|
+
|
|
170
|
+
The `deploy` skill references `smoke-test` as its final verification step:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# In deploy workflow — after successful deploy
|
|
174
|
+
DEPLOY_URL="$DEPLOY_URL" bash scripts/run-smoke.sh
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Configuration reference
|
|
178
|
+
|
|
179
|
+
| Variable | Default | Description |
|
|
180
|
+
|----------|---------|-------------|
|
|
181
|
+
| `SMOKE_CHECKS_FILE` | `smoke-checks.yaml` | Path to smoke checks YAML |
|
|
182
|
+
| `DEPLOY_URL` / `BASE_URL` | *(required)* | Base URL for all checks |
|
|
183
|
+
| `SMOKE_TIMEOUT` | `30` | Per-check timeout (seconds) |
|
|
184
|
+
| `SMOKE_RETRIES` | `0` | Number of retries on failure |
|
|
185
|
+
|
|
186
|
+
## Verification
|
|
187
|
+
|
|
188
|
+
→ verify: `test -f smoke-test/SKILL.md && grep -q 'name: smoke-test' smoke-test/SKILL.md && echo OK`
|
|
189
|
+
→ verify: `grep -qi 'smoke.checks.yaml\|checklist\|expected_status\|content_signal' smoke-test/SKILL.md && echo OK`
|
|
190
|
+
→ verify: `grep -ci 'pass\|fail\|summary\|report' smoke-test/SKILL.md | awk '{if($1>=2) print "OK"; else print "FAIL"}'`
|
|
191
|
+
→ verify: `grep -q 'smoke-test' SKILL-INDEX.md && echo OK`
|