@seanyao/roll 2026.424.4 → 2026.504.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/bin/roll +1 -1
- package/conventions/global/AGENTS.md +27 -0
- package/conventions/global/CLAUDE.md +22 -1
- package/conventions/templates/fullstack/CLAUDE.md +1 -1
- package/package.json +1 -1
- package/skills/roll-.qa/SKILL.md +1 -1
- package/skills/roll-debug/SKILL.md +252 -148
- package/skills/roll-debug/injectable-bb.js +263 -0
- package/skills/roll-doctor/SKILL.md +166 -0
- package/skills/roll-notes/SKILL.md +33 -3
- package/skills/roll-release/SKILL.md +3 -3
package/bin/roll
CHANGED
|
@@ -4,7 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
# Roll — AI Agent Convention Manager
|
|
5
5
|
# Single source of truth for how all AI coding agents behave.
|
|
6
6
|
|
|
7
|
-
VERSION="2026.
|
|
7
|
+
VERSION="2026.504.1"
|
|
8
8
|
ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
|
|
9
9
|
ROLL_CONFIG="${ROLL_HOME}/config.yaml"
|
|
10
10
|
ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
- User's language. Code/Git/Comments: English. UI: Chinese.
|
|
7
7
|
- Concise. No summaries/code-walking. Implementation invisible.
|
|
8
8
|
- Strategy (Why) OK; Tactics (How) NO. Outcomes only.
|
|
9
|
+
- **Ambiguity resolution**: When user says "explicit" in automation contexts,
|
|
10
|
+
interpret as "logged/observable with clear output", NOT "requiring manual
|
|
11
|
+
intervention". Confirm with one question if uncertain.
|
|
12
|
+
- **Bilingual output**: EN + ZH on separate lines, never inline.
|
|
13
|
+
```
|
|
14
|
+
Processing...
|
|
15
|
+
处理中...
|
|
16
|
+
```
|
|
17
|
+
Not: `Processing... 处理中...`
|
|
9
18
|
|
|
10
19
|
## 2. Code
|
|
11
20
|
- **TS**: Strict, no `any`. Functional hooks. Early returns.
|
|
@@ -20,5 +29,23 @@
|
|
|
20
29
|
|
|
21
30
|
## 4. Workflow
|
|
22
31
|
- **TCR**: Test -> Green = Commit / Red = Revert. No WIP commits.
|
|
32
|
+
- Before implementing: confirm exact files, test strategy, and commit message
|
|
33
|
+
draft with user. Do not write code until approved.
|
|
34
|
+
- Before claiming completion: verify in the target environment mentioned by
|
|
35
|
+
user (e.g., specific CLI tool, remote server, hardware platform).
|
|
23
36
|
- **Workspace**: `BACKLOG.md` index. `docs/features/` for details.
|
|
24
37
|
- **Done**: Push + CI passes + deployed. Local-only is not done.
|
|
38
|
+
|
|
39
|
+
## 5. Refactoring & Renames
|
|
40
|
+
|
|
41
|
+
Project-wide renames require systematic checking. Never assume find/replace
|
|
42
|
+
is sufficient. Execute in order:
|
|
43
|
+
|
|
44
|
+
1. **Code references** — imports, function names, string literals
|
|
45
|
+
2. **Documentation** — README, methodology, API docs
|
|
46
|
+
3. **Config files** — YAML frontmatter, package names, manifests
|
|
47
|
+
4. **Symlinks** — verify all resolve after rename
|
|
48
|
+
5. **Installer scripts** — update paths and references
|
|
49
|
+
6. **Shell environment** — remind user to reload or restart sessions
|
|
50
|
+
|
|
51
|
+
Confirm each phase clean before proceeding to the next.
|
|
@@ -17,12 +17,33 @@
|
|
|
17
17
|
|
|
18
18
|
## Claude Code-Specific
|
|
19
19
|
|
|
20
|
-
- When a project has Roll skills, use them (`$roll-design`, `$roll-
|
|
20
|
+
- When a project has Roll skills, use them (`$roll-design`, `$roll-build`, `$roll-fix`, etc.).
|
|
21
21
|
- Use plan mode for complex multi-step tasks before executing.
|
|
22
22
|
- Prefer Edit tool over Bash for file modifications.
|
|
23
23
|
- Use Agent tool with worktree isolation for parallel independent subtasks.
|
|
24
24
|
- When I say "帮我看下" or "处理下", go ahead and do it, not just analyze it.
|
|
25
25
|
|
|
26
|
+
## Verification and Testing
|
|
27
|
+
|
|
28
|
+
Before claiming any fix is complete, verify it works in the target environment
|
|
29
|
+
mentioned by the user. If they said a specific CLI tool, remote server, or
|
|
30
|
+
hardware platform, test there explicitly. Do not claim completion until verified.
|
|
31
|
+
|
|
32
|
+
## Configuration File Editing
|
|
33
|
+
|
|
34
|
+
When editing config files (YAML, TOML, JSON with schema):
|
|
35
|
+
1. Find official documentation or a verified working example first
|
|
36
|
+
2. Do not guess syntax
|
|
37
|
+
3. If no docs found after 2 searches, ask user for a reference config
|
|
38
|
+
4. Maximum 2 syntax attempts before escalating to research mode
|
|
39
|
+
|
|
40
|
+
## External Service Integration
|
|
41
|
+
|
|
42
|
+
For npm publishing, proxy configurations, or auth-dependent deployment:
|
|
43
|
+
- Stop after 2 failed attempts and ask user for preferred fallback
|
|
44
|
+
- Do not continue iterating on auth/proxy debugging without explicit direction
|
|
45
|
+
- If OIDC/token issues persist, immediately fallback to manual with explanation
|
|
46
|
+
|
|
26
47
|
## Frontend Default Stack
|
|
27
48
|
|
|
28
49
|
- React + shadcn/ui + Tailwind CSS is the default.
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
|
|
14
14
|
- Use `$roll-design` to plan features that span frontend and backend.
|
|
15
15
|
- When modifying API contracts, update both `api/types.ts` and `src/shared/types/` in the same commit.
|
|
16
|
-
- Use worktree isolation for parallel frontend/backend Actions in `$roll-
|
|
16
|
+
- Use worktree isolation for parallel frontend/backend Actions in `$roll-build`.
|
|
17
17
|
- Run `npm run build` to verify both frontend and backend compile before pushing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seanyao/roll",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.504.1",
|
|
4
4
|
"description": "Roll — Roll out features with AI agents",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "find tests/unit tests/integration -name '*.bats' | sort | xargs ./tests/helpers/bats-core/bin/bats"
|
package/skills/roll-.qa/SKILL.md
CHANGED
|
@@ -208,4 +208,4 @@ If project lacks Playwright setup:
|
|
|
208
208
|
|
|
209
209
|
- [Playwright Docs](https://playwright.dev/)
|
|
210
210
|
- [Visual Regression Guide](https://playwright.dev/docs/test-snapshots)
|
|
211
|
-
- Example implementation:
|
|
211
|
+
- Example implementation: `<owner>/<repo>/e2e/`
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: roll-debug
|
|
3
3
|
license: MIT
|
|
4
|
-
description: Universal web debugger.
|
|
4
|
+
description: Universal web debugger. Mounts a Black Box (BB) diagnostic probe on any page, collects rich diagnostics, analyzes root causes, and suggests fixes. Cleans up after itself.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Roll Debug
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Web debugging tool that treats the **Black Box (BB) as a diagnostic probe** — mounted when needed, unmounted when done. Combines diagnostic collection and analysis into a single workflow: **Mount → Collect → Analyze → Unmount**.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **
|
|
11
|
+
## Philosophy
|
|
12
|
+
|
|
13
|
+
- BB is a **diagnostic probe**, not a product feature. Pages do not need to integrate BB natively.
|
|
14
|
+
- For any web diagnosis, **mount BB first** (unless already present).
|
|
15
|
+
- The entire lifecycle is **explicit and visible**: you see when BB mounts, when it collects, and when it unmounts.
|
|
16
|
+
- A visible **BB button** appears on the page during diagnosis so you always know the probe is active.
|
|
14
17
|
|
|
15
18
|
## When to Use
|
|
16
19
|
|
|
@@ -31,15 +34,18 @@ Two collection modes:
|
|
|
31
34
|
## Quick Start
|
|
32
35
|
|
|
33
36
|
```bash
|
|
34
|
-
# Full workflow: collect + analyze +
|
|
37
|
+
# Full workflow: mount + collect + analyze + unmount (recommended)
|
|
35
38
|
$roll-debug https://example.com/page
|
|
36
39
|
|
|
37
40
|
# Collect data only, skip analysis
|
|
38
41
|
$roll-debug https://example.com/page --no-analyze
|
|
39
42
|
|
|
40
|
-
#
|
|
43
|
+
# Skip BB mount, use built-in universal collector
|
|
41
44
|
$roll-debug https://example.com/page --universal
|
|
42
45
|
|
|
46
|
+
# Use a custom BB SDK instead of the built-in stub
|
|
47
|
+
$roll-debug https://example.com/page --bb-sdk-url https://cdn.example.com/bb.js
|
|
48
|
+
|
|
43
49
|
# Collect + analyze + auto-fix
|
|
44
50
|
$roll-debug https://example.com/page --fix
|
|
45
51
|
|
|
@@ -51,44 +57,21 @@ $roll-debug https://site.com/page1,https://site.com/page2
|
|
|
51
57
|
$roll-debug --file urls.txt
|
|
52
58
|
```
|
|
53
59
|
|
|
54
|
-
##
|
|
55
|
-
|
|
56
|
-
### Mode 1: Native BB (Page has Black Box integrated)
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
Page with BB → Playwright clicks BB button → Download diagnostic JSON
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Requirements:
|
|
63
|
-
- Page has `[data-testid="bb-toggle"]` button
|
|
64
|
-
- Or exposes `window.__BB_DATA__`
|
|
65
|
-
- Or stores data in `localStorage.bb_diagnostic`
|
|
66
|
-
|
|
67
|
-
### Mode 2: Universal Diagnostic (No BB required)
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
Any page → Playwright injects collector → Gather console/network/DOM/screenshot
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Collects:
|
|
74
|
-
- Console logs (error/warn/info)
|
|
75
|
-
- Network requests (failed XHR/fetch, slow requests)
|
|
76
|
-
- DOM state (key elements visibility, HTML length)
|
|
77
|
-
- Screenshot (full page + viewport)
|
|
78
|
-
- Performance metrics (load time, FCP, LCP, render blocking)
|
|
79
|
-
- JavaScript errors with stack traces
|
|
80
|
-
|
|
81
|
-
## Full Workflow
|
|
60
|
+
## BB Probe Lifecycle
|
|
82
61
|
|
|
83
62
|
```
|
|
84
63
|
User: "Debug the page"
|
|
85
64
|
│
|
|
86
65
|
▼
|
|
87
66
|
┌─────────────────────────────────────┐
|
|
88
|
-
│ 1.
|
|
89
|
-
│ ├──
|
|
90
|
-
│ ├──
|
|
91
|
-
│ └──
|
|
67
|
+
│ 1. Mount BB Probe │
|
|
68
|
+
│ ├── Check: page already has BB? │
|
|
69
|
+
│ │ ├── Yes → reuse existing │
|
|
70
|
+
│ │ └── No → inject BB │
|
|
71
|
+
│ │ ├── Built-in stub (default)
|
|
72
|
+
│ │ └── Custom SDK (--bb-sdk-url)
|
|
73
|
+
│ ├── Wait for initialization │
|
|
74
|
+
│ └── BB button appears on page │
|
|
92
75
|
└──────────────────┬──────────────────┘
|
|
93
76
|
│
|
|
94
77
|
▼
|
|
@@ -97,6 +80,7 @@ User: "Debug the page"
|
|
|
97
80
|
│ ├── Console logs │
|
|
98
81
|
│ ├── Network requests │
|
|
99
82
|
│ ├── DOM state │
|
|
83
|
+
│ ├── Performance metrics │
|
|
100
84
|
│ └── Screenshot │
|
|
101
85
|
└──────────────────┬──────────────────┘
|
|
102
86
|
│
|
|
@@ -111,40 +95,82 @@ User: "Debug the page"
|
|
|
111
95
|
│
|
|
112
96
|
▼
|
|
113
97
|
┌─────────────────────────────────────┐
|
|
114
|
-
│ 4.
|
|
115
|
-
│ ├──
|
|
116
|
-
│ ├──
|
|
117
|
-
│
|
|
118
|
-
|
|
98
|
+
│ 4. Unmount BB Probe │
|
|
99
|
+
│ ├── Restore console/fetch/XHR │
|
|
100
|
+
│ ├── Remove BB button from DOM │
|
|
101
|
+
│ ├── Delete window.__BB_DATA__ │
|
|
102
|
+
│ └── Page state fully restored │
|
|
103
|
+
└──────────────────┬──────────────────┘
|
|
104
|
+
│
|
|
105
|
+
▼
|
|
106
|
+
Fix suggestions (or --fix to apply)
|
|
119
107
|
```
|
|
120
108
|
|
|
109
|
+
## Collection Modes
|
|
110
|
+
|
|
111
|
+
### Mode 1: Mounted BB (Default)
|
|
112
|
+
|
|
113
|
+
BB probe is mounted on the page — either reused from an existing BB or freshly injected.
|
|
114
|
+
|
|
115
|
+
**Visual indicator**: a red circular **BB** button appears at the bottom-right of the page.
|
|
116
|
+
|
|
117
|
+
**Data collected via BB interface**:
|
|
118
|
+
- Console logs (error/warn/info)
|
|
119
|
+
- Network requests (failed XHR/fetch, slow requests)
|
|
120
|
+
- DOM state (key elements visibility, HTML length)
|
|
121
|
+
- Performance metrics (load time, FCP, LCP)
|
|
122
|
+
- JavaScript errors with stack traces
|
|
123
|
+
|
|
124
|
+
**BB Sources**:
|
|
125
|
+
|
|
126
|
+
| Source | When Used | Capability |
|
|
127
|
+
|--------|-----------|------------|
|
|
128
|
+
| Existing native BB | Page already has `[data-testid="bb-toggle"]` or `window.__BB_DATA__` | Full app-specific metrics (contentState, audioState, etc.) |
|
|
129
|
+
| Built-in stub | Default when no BB present | Generic metrics (console, network, DOM, performance, errors) |
|
|
130
|
+
| Custom SDK | `--bb-sdk-url` provided | Determined by the SDK implementation |
|
|
131
|
+
|
|
132
|
+
### Mode 2: Universal Diagnostic (No BB)
|
|
133
|
+
|
|
134
|
+
When `--universal` is passed, skip BB mount entirely. Use Playwright's built-in event listeners directly.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
$roll-debug https://example.com/page --universal
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Useful when:
|
|
141
|
+
- You explicitly do not want to modify the page state
|
|
142
|
+
- The page has strict CSP that blocks script injection
|
|
143
|
+
- You need a quick check without probe overhead
|
|
144
|
+
|
|
121
145
|
## Usage Examples
|
|
122
146
|
|
|
123
|
-
### Example 1: Full auto-
|
|
147
|
+
### Example 1: Full auto-mount + analyze (default)
|
|
124
148
|
|
|
125
149
|
```bash
|
|
126
150
|
$roll-debug https://yyy.up.railway.app/story/cars/chapter/1
|
|
127
151
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
├──
|
|
135
|
-
├──
|
|
136
|
-
|
|
152
|
+
🔍 Diagnosing https://yyy.up.railway.app/story/cars/chapter/1
|
|
153
|
+
📡 Mounting BB probe...
|
|
154
|
+
├── Source: built-in stub
|
|
155
|
+
└── Status: ready (320ms)
|
|
156
|
+
└── BB button visible on page ✓
|
|
157
|
+
📊 Collecting data via BB...
|
|
158
|
+
├── Console: 3 errors, 5 warnings
|
|
159
|
+
├── Network: 2 failed requests
|
|
160
|
+
├── DOM: #app rendered, .content empty
|
|
161
|
+
└── Screenshot: saved to /tmp/bb-screenshot.png
|
|
162
|
+
🔬 Analyzing...
|
|
163
|
+
🧹 Unmounting BB probe... done
|
|
164
|
+
└── Page state restored ✓
|
|
137
165
|
|
|
138
166
|
Report: /tmp/bb-report.json
|
|
139
167
|
|
|
140
|
-
Analyzing...
|
|
141
|
-
|
|
142
168
|
## Diagnostic Analysis Report
|
|
143
169
|
|
|
144
170
|
### Basic Info
|
|
145
171
|
| Field | Value |
|
|
146
172
|
|-------|-------|
|
|
147
|
-
| Diagnostic Mode |
|
|
173
|
+
| Diagnostic Mode | mounted-bb (stub) |
|
|
148
174
|
| Page URL | https://yyy.up.railway.app/story/cars/chapter/1 |
|
|
149
175
|
|
|
150
176
|
### Key Findings
|
|
@@ -165,58 +191,58 @@ Modify Player.tsx line 45, change useEffect dependency
|
|
|
165
191
|
from `[chapter?.id]` to `[chapter?.number]`
|
|
166
192
|
```
|
|
167
193
|
|
|
168
|
-
### Example 2:
|
|
194
|
+
### Example 2: Reuse existing native BB
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
$roll-debug https://example.com/page
|
|
198
|
+
|
|
199
|
+
🔍 Diagnosing https://example.com/page
|
|
200
|
+
📡 Mounting BB probe...
|
|
201
|
+
├── Native BB detected
|
|
202
|
+
└── Reusing existing probe
|
|
203
|
+
📊 Collecting data via BB...
|
|
204
|
+
├── Console: 0 errors
|
|
205
|
+
├── Network: 0 failed
|
|
206
|
+
└── DOM: fully rendered
|
|
207
|
+
🔬 Analyzing...
|
|
208
|
+
🧹 Unmounting BB probe... skipped
|
|
209
|
+
└── Native BB left intact
|
|
210
|
+
|
|
211
|
+
No issues found. Page is healthy.
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Example 3: Universal mode (no BB mount)
|
|
169
215
|
|
|
170
216
|
```bash
|
|
171
217
|
$roll-debug https://example.com --universal
|
|
172
218
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
├── Console Errors: 2
|
|
177
|
-
│ ├── TypeError: Cannot read property 'id' of undefined
|
|
178
|
-
│ │ at Player.tsx:45
|
|
179
|
-
│ └── ReferenceError: AudioContext is not defined
|
|
180
|
-
├── Failed Network: 1
|
|
181
|
-
│ └── GET https://api.example.com/data 404
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
│ ├── #root: rendered
|
|
185
|
-
│ ├── .loading: still visible (timeout?)
|
|
186
|
-
│ └── .error-message: visible
|
|
187
|
-
├── Performance:
|
|
188
|
-
│ ├── DOMContentLoaded: 1.2s
|
|
189
|
-
│ ├── First Contentful Paint: 2.3s
|
|
190
|
-
│ └── Largest Contentful Paint: 4.5s
|
|
191
|
-
└── Screenshot: /tmp/bb-screenshot.png
|
|
219
|
+
🔍 Diagnosing https://example.com (universal mode)
|
|
220
|
+
📡 BB mount skipped (--universal)
|
|
221
|
+
📊 Collecting data via Playwright events...
|
|
222
|
+
├── Console Errors: 2
|
|
223
|
+
│ ├── TypeError: Cannot read property 'id' of undefined
|
|
224
|
+
│ │ at Player.tsx:45
|
|
225
|
+
│ └── ReferenceError: AudioContext is not defined
|
|
226
|
+
├── Failed Network: 1
|
|
227
|
+
│ └── GET https://api.example.com/data 404
|
|
228
|
+
└── Screenshot: /tmp/bb-screenshot.png
|
|
229
|
+
🔬 Analyzing...
|
|
192
230
|
|
|
193
231
|
Report: /tmp/bb-report.json
|
|
194
232
|
|
|
195
|
-
Analyzing...
|
|
196
|
-
|
|
197
233
|
### Key Findings
|
|
198
234
|
| Metric | Value | Status |
|
|
199
235
|
|--------|-------|--------|
|
|
200
236
|
| Console Errors | 2 | Critical |
|
|
201
237
|
| Network Failed | 1 | Critical |
|
|
202
|
-
| DOM Rendering | Partial | Warning |
|
|
203
|
-
| Load Time (LCP) | 4.5s | Slow |
|
|
204
|
-
|
|
205
|
-
### Diagnosis Conclusion
|
|
206
|
-
API endpoint returning 404 causes content not to load.
|
|
207
|
-
AudioContext undefined suggests missing polyfill or browser incompatibility.
|
|
208
|
-
|
|
209
|
-
### Suggested Fix
|
|
210
|
-
1. Fix API route for /data endpoint (404)
|
|
211
|
-
2. Add AudioContext polyfill or guard with `typeof AudioContext !== 'undefined'`
|
|
212
238
|
```
|
|
213
239
|
|
|
214
|
-
### Example
|
|
240
|
+
### Example 4: Analyze existing report file
|
|
215
241
|
|
|
216
242
|
```bash
|
|
217
243
|
$roll-debug --report /tmp/bb-report.json
|
|
218
244
|
|
|
219
|
-
Reading report: /tmp/bb-report.json (mode:
|
|
245
|
+
Reading report: /tmp/bb-report.json (mode: mounted-bb)
|
|
220
246
|
|
|
221
247
|
### Key Findings
|
|
222
248
|
...
|
|
@@ -226,11 +252,13 @@ Reading report: /tmp/bb-report.json (mode: universal)
|
|
|
226
252
|
|
|
227
253
|
| Format | Source | Description |
|
|
228
254
|
|--------|--------|-------------|
|
|
229
|
-
|
|
|
230
|
-
|
|
|
255
|
+
| Mounted BB (stub) | Injected built-in stub | `window.__BB_DATA__` via injectable-bb.js |
|
|
256
|
+
| Mounted BB (native) | Page with existing Black Box | `window.__BB_DATA__` or `localStorage.bb_diagnostic` |
|
|
257
|
+
| Mounted BB (custom) | Custom SDK via `--bb-sdk-url` | Determined by SDK |
|
|
258
|
+
| Universal | Playwright native events | Direct event listener data |
|
|
231
259
|
| Legacy | Old diagnostic files | Backward compatible |
|
|
232
260
|
|
|
233
|
-
###
|
|
261
|
+
### Mounted BB Mode Fields
|
|
234
262
|
|
|
235
263
|
```javascript
|
|
236
264
|
const bbData = report.diagnostic.bbData;
|
|
@@ -240,6 +268,11 @@ bbData.audioState?.src
|
|
|
240
268
|
bbData.audioState?.error
|
|
241
269
|
bbData.hasAudio
|
|
242
270
|
bbData.errors
|
|
271
|
+
bbData.console.errors
|
|
272
|
+
bbData.console.warnings
|
|
273
|
+
bbData.network.failed
|
|
274
|
+
bbData.dom.keyElements
|
|
275
|
+
bbData.performance.loadComplete
|
|
243
276
|
```
|
|
244
277
|
|
|
245
278
|
### Universal Mode Fields
|
|
@@ -255,7 +288,6 @@ d.dom['#root']
|
|
|
255
288
|
d.dom.htmlLength
|
|
256
289
|
d.performance.loadComplete
|
|
257
290
|
d.performance.domContentLoaded
|
|
258
|
-
d.errors
|
|
259
291
|
```
|
|
260
292
|
|
|
261
293
|
## Analysis Report Template
|
|
@@ -266,7 +298,8 @@ d.errors
|
|
|
266
298
|
### Basic Info
|
|
267
299
|
| Field | Value |
|
|
268
300
|
|-------|-------|
|
|
269
|
-
| Diagnostic Mode | {
|
|
301
|
+
| Diagnostic Mode | {mounted-bb / universal} |
|
|
302
|
+
| BB Source | {native / stub / custom-sdk} |
|
|
270
303
|
| Page URL | {url} |
|
|
271
304
|
| Collected At | {timestamp} |
|
|
272
305
|
|
|
@@ -309,62 +342,123 @@ d.errors
|
|
|
309
342
|
|
|
310
343
|
## Implementation Notes
|
|
311
344
|
|
|
312
|
-
###
|
|
313
|
-
|
|
314
|
-
When page has no BB, inject a lightweight collector via `page.evaluate()`:
|
|
345
|
+
### BB Mount Flow (Playwright)
|
|
315
346
|
|
|
316
347
|
```javascript
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
348
|
+
// Pseudocode for AI agent execution
|
|
349
|
+
async function diagnose(page, url, args) {
|
|
350
|
+
log(`🔍 Diagnosing ${url}`);
|
|
351
|
+
|
|
352
|
+
// Step 1: Mount
|
|
353
|
+
const bbState = await mountBB(page, args);
|
|
354
|
+
log(`📡 Mounting BB probe...`);
|
|
355
|
+
log(` ├── Source: ${bbState.source}`); // native / stub / custom
|
|
356
|
+
log(` └── Status: ${bbState.ready ? 'ready' : 'failed'}`);
|
|
357
|
+
|
|
358
|
+
if (bbState.ready && bbState.source !== 'native') {
|
|
359
|
+
log(` └── BB button visible on page ✓`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Step 2: Collect
|
|
363
|
+
log(`📊 Collecting data via BB...`);
|
|
364
|
+
const data = await collectViaBB(page);
|
|
365
|
+
|
|
366
|
+
// Step 3: Analyze
|
|
367
|
+
log(`🔬 Analyzing...`);
|
|
368
|
+
const analysis = await analyze(data);
|
|
369
|
+
|
|
370
|
+
// Step 4: Unmount (unless native BB)
|
|
371
|
+
if (bbState.source !== 'native') {
|
|
372
|
+
log(`🧹 Unmounting BB probe...`);
|
|
373
|
+
const ok = await page.evaluate(() => window.__BB_UNMOUNT__?.());
|
|
374
|
+
log(` └── ${ok ? 'done' : 'failed'}`);
|
|
375
|
+
log(` └── Page state restored ✓`);
|
|
376
|
+
} else {
|
|
377
|
+
log(`🧹 Unmounting BB probe... skipped`);
|
|
378
|
+
log(` └── Native BB left intact`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return analysis;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function mountBB(page, args) {
|
|
385
|
+
// Check for existing BB
|
|
386
|
+
const hasNative = await page.evaluate(() =>
|
|
387
|
+
!!document.querySelector('[data-testid="bb-toggle"]') || !!window.__BB_DATA__
|
|
388
|
+
);
|
|
389
|
+
if (hasNative) {
|
|
390
|
+
return { source: 'native', ready: true };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (args.universal) {
|
|
394
|
+
return { source: 'universal', ready: false };
|
|
395
|
+
}
|
|
339
396
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
397
|
+
// Inject BB
|
|
398
|
+
try {
|
|
399
|
+
if (args.bbSdkUrl) {
|
|
400
|
+
await page.addScriptTag({ url: args.bbSdkUrl });
|
|
401
|
+
} else {
|
|
402
|
+
const stubPath = path.join(__dirname, 'injectable-bb.js');
|
|
403
|
+
await page.addScriptTag({ path: stubPath });
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Poll for readiness
|
|
407
|
+
const ready = await poll(
|
|
408
|
+
() => page.evaluate(() => !!window.__BB_DATA__),
|
|
409
|
+
{ timeout: 5000, interval: 200 }
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
return { source: args.bbSdkUrl ? 'custom' : 'stub', ready };
|
|
413
|
+
} catch (e) {
|
|
414
|
+
return { source: 'stub', ready: false, error: e.message };
|
|
348
415
|
}
|
|
349
|
-
}
|
|
416
|
+
}
|
|
350
417
|
```
|
|
351
418
|
|
|
352
|
-
|
|
419
|
+
### Built-in Stub (`injectable-bb.js`)
|
|
420
|
+
|
|
421
|
+
The stub is injected via `page.addScriptTag({ path })` when no native BB exists.
|
|
422
|
+
|
|
423
|
+
**Capabilities**:
|
|
424
|
+
- Hooks `console.*` with internal error firewall (stub bugs never leak to page)
|
|
425
|
+
- Hooks `fetch` and `XMLHttpRequest` transparently — original behavior fully preserved
|
|
426
|
+
- Listens for `error` and `unhandledrejection`
|
|
427
|
+
- Captures Performance Navigation Timing + FCP + LCP
|
|
428
|
+
- Captures DOM state (title, HTML length, key element visibility)
|
|
429
|
+
- Renders a visible **BB** button on the page
|
|
430
|
+
|
|
431
|
+
**Cleanup**:
|
|
432
|
+
- `window.__BB_UNMOUNT__()` restores all modified globals to their original references
|
|
433
|
+
- Removes the BB button from DOM
|
|
434
|
+
- Deletes `window.__BB_DATA__` and `window.__BB_UNMOUNT__`
|
|
435
|
+
|
|
436
|
+
### Universal Mode (No BB)
|
|
437
|
+
|
|
438
|
+
When `--universal` is used, collect via Playwright native events:
|
|
439
|
+
|
|
440
|
+
```javascript
|
|
441
|
+
page.on('console', msg => ...);
|
|
442
|
+
page.on('requestfailed', req => ...);
|
|
443
|
+
page.on('response', res => ...);
|
|
444
|
+
page.on('pageerror', err => ...);
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
No page state is modified.
|
|
353
448
|
|
|
354
449
|
## Data Output Formats
|
|
355
450
|
|
|
356
|
-
###
|
|
451
|
+
### Mounted BB Mode
|
|
357
452
|
|
|
358
453
|
```json
|
|
359
454
|
{
|
|
360
|
-
"mode": "
|
|
455
|
+
"mode": "mounted-bb",
|
|
456
|
+
"bbSource": "stub",
|
|
361
457
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
362
458
|
"url": "https://example.com/page",
|
|
363
459
|
"bbData": {},
|
|
364
|
-
"
|
|
365
|
-
|
|
366
|
-
"network": []
|
|
367
|
-
}
|
|
460
|
+
"mountedAt": 1705315800000,
|
|
461
|
+
"unmountedAt": 1705315805000
|
|
368
462
|
}
|
|
369
463
|
```
|
|
370
464
|
|
|
@@ -389,7 +483,7 @@ The injected collector only exists in the Playwright browser context — no clea
|
|
|
389
483
|
"title": "Page Title",
|
|
390
484
|
"htmlLength": 2340,
|
|
391
485
|
"keyElements": {
|
|
392
|
-
"#root": {"exists": true, "visible": true, "
|
|
486
|
+
"#root": {"exists": true, "visible": true, "text": "..."},
|
|
393
487
|
".error": {"exists": false}
|
|
394
488
|
}
|
|
395
489
|
},
|
|
@@ -409,16 +503,26 @@ The injected collector only exists in the Playwright browser context — no clea
|
|
|
409
503
|
|
|
410
504
|
## Capability Comparison
|
|
411
505
|
|
|
412
|
-
| Feature |
|
|
413
|
-
|
|
414
|
-
|
|
|
415
|
-
|
|
|
416
|
-
|
|
|
417
|
-
|
|
|
418
|
-
|
|
|
419
|
-
|
|
|
420
|
-
|
|
|
421
|
-
|
|
|
506
|
+
| Feature | Mounted BB (stub) | Mounted BB (native) | Universal |
|
|
507
|
+
|---------|-------------------|---------------------|-----------|
|
|
508
|
+
| Page modification | Yes (mount/unmount) | No (already there) | No |
|
|
509
|
+
| Visible BB button | Yes | If native has one | No |
|
|
510
|
+
| Console logs | Yes | Yes | Yes |
|
|
511
|
+
| Network data | Yes | Yes | Yes |
|
|
512
|
+
| DOM state | Detailed | Detailed | Key elements |
|
|
513
|
+
| App-specific metrics | No | Yes | No |
|
|
514
|
+
| Screenshot | Yes | Yes | Yes |
|
|
515
|
+
| Performance metrics | Yes | Yes | Yes |
|
|
516
|
+
| Works offline | Yes | Yes | Yes |
|
|
517
|
+
| Cleanup on exit | Yes (full restore) | N/A | N/A |
|
|
518
|
+
|
|
519
|
+
## Safety & Cleanup Guarantees
|
|
520
|
+
|
|
521
|
+
1. **Stub errors are firewalled** — every hook wraps its internal logic in try/catch. A bug in the stub cannot crash the page.
|
|
522
|
+
2. **Original behavior preserved** — fetch/XHR wrappers return the exact same values/throw the exact same errors as the originals.
|
|
523
|
+
3. **Full unmount** — `__BB_UNMOUNT__()` restores console, fetch, XHR, removes listeners, removes DOM element, and deletes globals.
|
|
524
|
+
4. **Native BB untouched** — if a page already has BB, it is reused but never unmounted.
|
|
525
|
+
5. **CSP fallback** — if script injection fails (CSP), automatically falls back to Universal mode.
|
|
422
526
|
|
|
423
527
|
## Integration with Build Skills
|
|
424
528
|
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// Roll Debug — Injectable BB Diagnostic Stub
|
|
2
|
+
// Injected into page context by Playwright when native BB is absent.
|
|
3
|
+
// Exposes window.__BB_DATA__ and [data-testid="bb-toggle"] for unified collection.
|
|
4
|
+
// Fully unmountable via window.__BB_UNMOUNT__().
|
|
5
|
+
|
|
6
|
+
(function () {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
if (window.__BB_DATA__) return; // Already mounted
|
|
10
|
+
|
|
11
|
+
// ─── Backup originals ───
|
|
12
|
+
const _orig = {
|
|
13
|
+
console: {},
|
|
14
|
+
fetch: window.fetch,
|
|
15
|
+
XHR_open: XMLHttpRequest.prototype.open,
|
|
16
|
+
XHR_send: XMLHttpRequest.prototype.send,
|
|
17
|
+
};
|
|
18
|
+
['error', 'warn', 'log', 'info'].forEach((m) => {
|
|
19
|
+
_orig.console[m] = console[m];
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// ─── BB State ───
|
|
23
|
+
const BB = {
|
|
24
|
+
version: 'stub-1.0',
|
|
25
|
+
mountedAt: Date.now(),
|
|
26
|
+
console: { errors: [], warnings: [], logs: [] },
|
|
27
|
+
network: { failed: [], slow: [], all: [] },
|
|
28
|
+
errors: [],
|
|
29
|
+
dom: {},
|
|
30
|
+
performance: {},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ─── Console hooks (with internal error firewall) ───
|
|
34
|
+
['error', 'warn', 'log', 'info'].forEach((m) => {
|
|
35
|
+
const key = m === 'error' ? 'errors' : m === 'warn' ? 'warnings' : 'logs';
|
|
36
|
+
const orig = _orig.console[m];
|
|
37
|
+
console[m] = function bbHookedConsole(...args) {
|
|
38
|
+
try {
|
|
39
|
+
BB.console[key].push({
|
|
40
|
+
message: args
|
|
41
|
+
.map((a) => {
|
|
42
|
+
try {
|
|
43
|
+
return typeof a === 'object' ? JSON.stringify(a) : String(a);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
return '[unstringifiable]';
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.join(' '),
|
|
49
|
+
timestamp: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
} catch (e) {
|
|
52
|
+
/* swallow stub internal error */
|
|
53
|
+
}
|
|
54
|
+
return orig.apply(this, args);
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// ─── Fetch hook (transparent wrapper) ───
|
|
59
|
+
const origFetch = _orig.fetch;
|
|
60
|
+
window.fetch = function bbHookedFetch(...args) {
|
|
61
|
+
const start = Date.now();
|
|
62
|
+
const url =
|
|
63
|
+
typeof args[0] === 'string'
|
|
64
|
+
? args[0]
|
|
65
|
+
: args[0]?.url || '[unknown]';
|
|
66
|
+
const method = args[1]?.method || 'GET';
|
|
67
|
+
|
|
68
|
+
return origFetch.apply(this, args).then(
|
|
69
|
+
(res) => {
|
|
70
|
+
try {
|
|
71
|
+
const duration = Date.now() - start;
|
|
72
|
+
const entry = { url, status: res.status, duration, method };
|
|
73
|
+
BB.network.all.push(entry);
|
|
74
|
+
if (!res.ok) BB.network.failed.push(entry);
|
|
75
|
+
if (duration > 3000) BB.network.slow.push(entry);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
/* swallow */
|
|
78
|
+
}
|
|
79
|
+
return res;
|
|
80
|
+
},
|
|
81
|
+
(err) => {
|
|
82
|
+
try {
|
|
83
|
+
BB.network.failed.push({
|
|
84
|
+
url,
|
|
85
|
+
error: err.message,
|
|
86
|
+
duration: Date.now() - start,
|
|
87
|
+
method,
|
|
88
|
+
});
|
|
89
|
+
} catch (e) {
|
|
90
|
+
/* swallow */
|
|
91
|
+
}
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
window.fetch.toString = () => 'function fetch() { [native code] }';
|
|
98
|
+
} catch (e) {
|
|
99
|
+
/* ignore */
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── XHR hook ───
|
|
103
|
+
XMLHttpRequest.prototype.open = function bbHookedOpen(method, url, ...rest) {
|
|
104
|
+
this._bb = { method, url: String(url), start: null };
|
|
105
|
+
return _orig.XHR_open.call(this, method, url, ...rest);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
XMLHttpRequest.prototype.send = function bbHookedSend(...args) {
|
|
109
|
+
if (this._bb) this._bb.start = Date.now();
|
|
110
|
+
const handler = () => {
|
|
111
|
+
if (!this._bb) return;
|
|
112
|
+
try {
|
|
113
|
+
const duration = Date.now() - this._bb.start;
|
|
114
|
+
const entry = {
|
|
115
|
+
url: this._bb.url,
|
|
116
|
+
status: this.status,
|
|
117
|
+
duration,
|
|
118
|
+
method: this._bb.method,
|
|
119
|
+
};
|
|
120
|
+
BB.network.all.push(entry);
|
|
121
|
+
if (this.status >= 400 || this.status === 0)
|
|
122
|
+
BB.network.failed.push(entry);
|
|
123
|
+
if (duration > 3000) BB.network.slow.push(entry);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
/* swallow */
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
this.addEventListener('loadend', handler, { once: true });
|
|
129
|
+
return _orig.XHR_send.apply(this, args);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ─── JS Error listeners ───
|
|
133
|
+
const onError = (e) => {
|
|
134
|
+
try {
|
|
135
|
+
BB.errors.push({
|
|
136
|
+
message: e.message,
|
|
137
|
+
stack: e.error?.stack,
|
|
138
|
+
timestamp: Date.now(),
|
|
139
|
+
});
|
|
140
|
+
} catch (e) {
|
|
141
|
+
/* swallow */
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const onRejection = (e) => {
|
|
145
|
+
try {
|
|
146
|
+
BB.errors.push({
|
|
147
|
+
message: e.reason?.message || String(e.reason),
|
|
148
|
+
stack: e.reason?.stack,
|
|
149
|
+
timestamp: Date.now(),
|
|
150
|
+
});
|
|
151
|
+
} catch (e) {
|
|
152
|
+
/* swallow */
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
window.addEventListener('error', onError);
|
|
156
|
+
window.addEventListener('unhandledrejection', onRejection);
|
|
157
|
+
|
|
158
|
+
// ─── Performance ───
|
|
159
|
+
const capturePerf = () => {
|
|
160
|
+
try {
|
|
161
|
+
const nav = performance.getEntriesByType('navigation')[0];
|
|
162
|
+
BB.performance = {
|
|
163
|
+
domContentLoaded: nav?.domContentLoadedEventEnd,
|
|
164
|
+
loadComplete: nav?.loadEventEnd,
|
|
165
|
+
firstContentfulPaint:
|
|
166
|
+
performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
|
|
167
|
+
largestContentfulPaint: performance
|
|
168
|
+
.getEntriesByType('largest-contentful-paint')
|
|
169
|
+
.pop()?.startTime,
|
|
170
|
+
};
|
|
171
|
+
} catch (e) {
|
|
172
|
+
/* swallow */
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
if (document.readyState === 'complete') {
|
|
176
|
+
capturePerf();
|
|
177
|
+
} else {
|
|
178
|
+
window.addEventListener('load', capturePerf, { once: true });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ─── DOM Capture ───
|
|
182
|
+
function captureDOM() {
|
|
183
|
+
try {
|
|
184
|
+
const info = (sel) => {
|
|
185
|
+
const el = document.querySelector(sel);
|
|
186
|
+
return el
|
|
187
|
+
? {
|
|
188
|
+
exists: true,
|
|
189
|
+
visible: el.offsetParent !== null,
|
|
190
|
+
text: el.textContent?.slice(0, 200),
|
|
191
|
+
}
|
|
192
|
+
: { exists: false };
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
title: document.title,
|
|
196
|
+
url: location.href,
|
|
197
|
+
htmlLength: document.documentElement.innerHTML.length,
|
|
198
|
+
keyElements: {
|
|
199
|
+
'#root': info('#root'),
|
|
200
|
+
'#app': info('#app'),
|
|
201
|
+
'[data-testid="error"]': info('[data-testid="error"]'),
|
|
202
|
+
'.error': info('.error'),
|
|
203
|
+
'.loading': info('.loading'),
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
} catch (e) {
|
|
207
|
+
return { error: 'DOM capture failed', url: location.href };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ─── Public API ───
|
|
212
|
+
BB.getData = () => ({ ...BB, dom: captureDOM(), collectedAt: Date.now() });
|
|
213
|
+
window.__BB_DATA__ = BB;
|
|
214
|
+
|
|
215
|
+
// ─── Visible BB toggle button ───
|
|
216
|
+
let btn;
|
|
217
|
+
if (document.body) {
|
|
218
|
+
btn = document.createElement('button');
|
|
219
|
+
btn.dataset.testid = 'bb-toggle';
|
|
220
|
+
btn.textContent = 'BB';
|
|
221
|
+
btn.title = 'Black Box Diagnostic Probe — click to download report';
|
|
222
|
+
btn.style.cssText =
|
|
223
|
+
'position:fixed;bottom:12px;right:12px;z-index:99999;' +
|
|
224
|
+
'width:36px;height:36px;border-radius:50%;border:none;' +
|
|
225
|
+
'background:#ff4444;color:#fff;font-size:11px;font-weight:bold;' +
|
|
226
|
+
'font-family:sans-serif;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.3);' +
|
|
227
|
+
'display:flex;align-items:center;justify-content:center;' +
|
|
228
|
+
'opacity:0.85;transition:opacity 0.2s;';
|
|
229
|
+
btn.onmouseenter = () => (btn.style.opacity = '1');
|
|
230
|
+
btn.onmouseleave = () => (btn.style.opacity = '0.85');
|
|
231
|
+
btn.onclick = () => {
|
|
232
|
+
const data = BB.getData();
|
|
233
|
+
const blob = new Blob([JSON.stringify(data, null, 2)], {
|
|
234
|
+
type: 'application/json',
|
|
235
|
+
});
|
|
236
|
+
const a = document.createElement('a');
|
|
237
|
+
a.href = URL.createObjectURL(blob);
|
|
238
|
+
a.download = `bb-diagnostic-${Date.now()}.json`;
|
|
239
|
+
a.click();
|
|
240
|
+
};
|
|
241
|
+
document.body.appendChild(btn);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ─── Unmount (restore page to original state) ───
|
|
245
|
+
window.__BB_UNMOUNT__ = function () {
|
|
246
|
+
try {
|
|
247
|
+
['error', 'warn', 'log', 'info'].forEach(
|
|
248
|
+
(m) => (console[m] = _orig.console[m])
|
|
249
|
+
);
|
|
250
|
+
window.fetch = _orig.fetch;
|
|
251
|
+
XMLHttpRequest.prototype.open = _orig.XHR_open;
|
|
252
|
+
XMLHttpRequest.prototype.send = _orig.XHR_send;
|
|
253
|
+
window.removeEventListener('error', onError);
|
|
254
|
+
window.removeEventListener('unhandledrejection', onRejection);
|
|
255
|
+
if (btn) btn.remove();
|
|
256
|
+
delete window.__BB_DATA__;
|
|
257
|
+
delete window.__BB_UNMOUNT__;
|
|
258
|
+
return true;
|
|
259
|
+
} catch (e) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
})();
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: roll-doctor
|
|
3
|
+
license: MIT
|
|
4
|
+
description: "Diagnose Roll toolchain health. Checks skill files, YAML frontmatter, symlinks, conventions sync, template integrity, and config validity."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Roll Doctor
|
|
8
|
+
|
|
9
|
+
诊断 Roll 工具链健康状态。快速定位 skill 不工作、convention 不同步、symlink 断裂等问题。
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- "roll 怎么不工作了"
|
|
14
|
+
- "skill 找不到了"
|
|
15
|
+
- "convention 没有同步"
|
|
16
|
+
- "$roll-debug 不响应"
|
|
17
|
+
- 任何 Roll 相关功能异常
|
|
18
|
+
|
|
19
|
+
## Checks
|
|
20
|
+
|
|
21
|
+
### 1. Roll Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Verify ROLL_HOME structure
|
|
25
|
+
ls -la ~/.roll/
|
|
26
|
+
ls -la ~/.roll/skills/
|
|
27
|
+
ls -la ~/.roll/conventions/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Expected:
|
|
31
|
+
- `~/.roll/` exists
|
|
32
|
+
- `~/.roll/skills/` has roll-* directories
|
|
33
|
+
- `~/.roll/conventions/global/` has AGENTS.md, CLAUDE.md, etc.
|
|
34
|
+
- `~/.roll/conventions/templates/` has backend-service, cli, frontend-only, fullstack
|
|
35
|
+
|
|
36
|
+
### 2. Skill Health
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# List all skills and check YAML frontmatter
|
|
40
|
+
for f in ~/.roll/skills/*/SKILL.md; do
|
|
41
|
+
echo "=== $(basename $(dirname $f)) ==="
|
|
42
|
+
head -5 "$f"
|
|
43
|
+
done
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Check:
|
|
47
|
+
- Each skill directory has `SKILL.md`
|
|
48
|
+
- YAML frontmatter has `name:` and `description:`
|
|
49
|
+
- No duplicate `name` values across skills
|
|
50
|
+
- File is readable markdown
|
|
51
|
+
|
|
52
|
+
### 3. Symlinks
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Claude Code
|
|
56
|
+
ls -la ~/.claude/skills/roll-*
|
|
57
|
+
|
|
58
|
+
# Gemini (if exists)
|
|
59
|
+
ls -la ~/.gemini/skills/roll-* 2>/dev/null
|
|
60
|
+
|
|
61
|
+
# Trae (if exists)
|
|
62
|
+
ls -la ~/.trae/skills/roll-* 2>/dev/null
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Check:
|
|
66
|
+
- Each symlink exists
|
|
67
|
+
- Each symlink points to valid target in `~/.roll/skills/`
|
|
68
|
+
- No broken symlinks (red in ls output)
|
|
69
|
+
|
|
70
|
+
### 4. Conventions Sync
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Check roll.md sync
|
|
74
|
+
diff ~/.roll/conventions/global/CLAUDE.md ~/.claude/roll.md
|
|
75
|
+
|
|
76
|
+
# Check CLAUDE.md includes @roll.md
|
|
77
|
+
grep "@roll.md" ~/.claude/CLAUDE.md
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Check:
|
|
81
|
+
- `~/.claude/roll.md` exists and matches `~/.roll/conventions/global/CLAUDE.md`
|
|
82
|
+
- `~/.claude/CLAUDE.md` contains `@roll.md` reference
|
|
83
|
+
- `~/.claude/CLAUDE.md` is not missing or stale
|
|
84
|
+
|
|
85
|
+
### 5. Template Integrity
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Global conventions
|
|
89
|
+
ls ~/.roll/conventions/global/
|
|
90
|
+
|
|
91
|
+
# Project type templates
|
|
92
|
+
for t in backend-service cli frontend-only fullstack; do
|
|
93
|
+
ls ~/.roll/conventions/templates/$t/
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
# New project template
|
|
97
|
+
ls ~/.roll/template/
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Check:
|
|
101
|
+
- All expected files present in each directory
|
|
102
|
+
- No missing AGENTS.md, CLAUDE.md, or GEMINI.md
|
|
103
|
+
|
|
104
|
+
### 6. Config
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
cat ~/.roll/config.yaml
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Check:
|
|
111
|
+
- File exists and is valid YAML
|
|
112
|
+
- Has required `ai_tool_name` or `ai_claude` entries
|
|
113
|
+
|
|
114
|
+
## Report Format
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
🔬 Roll Doctor Report
|
|
118
|
+
|
|
119
|
+
[✓/✗] Roll installation
|
|
120
|
+
[✓/✗] Skills (N skills checked)
|
|
121
|
+
[✓/✗] Symlinks (N tools checked)
|
|
122
|
+
[✓/✗] Conventions sync
|
|
123
|
+
[✓/✗] Templates integrity
|
|
124
|
+
[✓/✗] Config
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
Issues found: N
|
|
128
|
+
|
|
129
|
+
1. [severity] description → fix command
|
|
130
|
+
2. [severity] description → fix command
|
|
131
|
+
|
|
132
|
+
Recommendation: ...
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Severity Levels
|
|
136
|
+
|
|
137
|
+
| Level | Meaning | Example |
|
|
138
|
+
|-------|---------|---------|
|
|
139
|
+
| 🔴 Critical | Skill completely broken | Broken symlink, missing SKILL.md |
|
|
140
|
+
| 🟡 Warning | Partial functionality | Stale convention, outdated roll.md |
|
|
141
|
+
| 🟢 Info | Recommendations | Missing optional tool symlink |
|
|
142
|
+
|
|
143
|
+
## Auto-Fix
|
|
144
|
+
|
|
145
|
+
If user confirms, automatically fix safe issues:
|
|
146
|
+
|
|
147
|
+
- **Broken/missing symlinks** → `roll setup` (recreates all)
|
|
148
|
+
- **Stale conventions** → `roll sync conventions`
|
|
149
|
+
- **Missing template files** → warn user to re-install
|
|
150
|
+
|
|
151
|
+
Never auto-fix without user confirmation for:
|
|
152
|
+
- Overwriting `~/.claude/CLAUDE.md` (may have user customizations)
|
|
153
|
+
- Deleting files
|
|
154
|
+
|
|
155
|
+
## Quick Fix Commands
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Recreate all symlinks and sync conventions
|
|
159
|
+
roll setup
|
|
160
|
+
|
|
161
|
+
# Sync only conventions
|
|
162
|
+
roll sync conventions
|
|
163
|
+
|
|
164
|
+
# Reinstall (nuclear option)
|
|
165
|
+
curl -fsSL https://raw.githubusercontent.com/seanyao/roll/main/install.sh | bash
|
|
166
|
+
```
|
|
@@ -30,9 +30,13 @@ $roll-notes 今天的 code review 给了很好的反馈
|
|
|
30
30
|
|
|
31
31
|
1. **Determine file path**: `notes/YYYY-MM-DD.md` relative to project root
|
|
32
32
|
2. **Get current time**: Use `Asia/Shanghai` timezone (`TZ=Asia/Shanghai date`)
|
|
33
|
-
3. **
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
3. **Read existing entries for style**: Before writing, read the last 2–3 entries
|
|
34
|
+
in the same file. Analyze their style: heading format, voice/tone,
|
|
35
|
+
paragraph structure, emoji usage, code block conventions, sign-off pattern.
|
|
36
|
+
New entries must continue this style — do not invent new structural patterns.
|
|
37
|
+
4. **Append**: Add new entry at end of file — never overwrite existing content
|
|
38
|
+
5. **Create if missing**: If file doesn't exist, create with a `# YYYY-MM-DD — <one-line summary>` header
|
|
39
|
+
6. **Free format**: Paragraph, list, code block — whatever fits the moment
|
|
36
40
|
|
|
37
41
|
## File format
|
|
38
42
|
|
|
@@ -54,8 +58,34 @@ $roll-notes 今天的 code review 给了很好的反馈
|
|
|
54
58
|
...
|
|
55
59
|
```
|
|
56
60
|
|
|
61
|
+
## 写作风格(强制)
|
|
62
|
+
|
|
63
|
+
**禁止干巴巴的 checklist。禁止只有结论没有过程。必须有人的声音。**
|
|
64
|
+
|
|
65
|
+
### 叙事驱动
|
|
66
|
+
以时间线和事件推进为主线,像讲故事一样记录。不是 `补了功能,pytest 绿`,而是 `"电池剩余电量在哪里看?"我一愣,US-PLAT-003 的 AC 明明写了...`
|
|
67
|
+
|
|
68
|
+
### 技术细节嵌入叙事
|
|
69
|
+
代码、数据、命令行是故事的一部分,自然插入,不是附录。
|
|
70
|
+
|
|
71
|
+
### 对话与互动
|
|
72
|
+
记录用户的反馈、提问、情绪。日记是对话记录,不是独白。
|
|
73
|
+
|
|
74
|
+
### 诚实记录错误
|
|
75
|
+
失败、困惑、教训比成功更值得记录。
|
|
76
|
+
|
|
77
|
+
### 标题有画面感
|
|
78
|
+
不是功能清单。如 `每秒 20 行的噪音`、`22 个文件挤在一个抽屉里`。
|
|
79
|
+
|
|
80
|
+
### 结尾有收束
|
|
81
|
+
最后一段回到人的状态。如 `"值了"。"睡了"。push。`
|
|
82
|
+
|
|
57
83
|
## Rules
|
|
58
84
|
|
|
85
|
+
- **Style continuity**: Match the EXACT style, tone, and formatting of previous
|
|
86
|
+
entries in the same file. Do not invent new structural patterns or section
|
|
87
|
+
headers. If the file already has entries, analyze them first and confirm your
|
|
88
|
+
understanding of their style before writing.
|
|
59
89
|
- **No planning**: Never write "明日待办" or "下一步"
|
|
60
90
|
- **No summaries**: Never write "今日收获" or "经验教训"
|
|
61
91
|
- **Pure record**: What happened, as-is — honest, immediate, rough is fine
|
|
@@ -10,7 +10,7 @@ One-command publish flow for roll maintainers.
|
|
|
10
10
|
|
|
11
11
|
## When Not to Use
|
|
12
12
|
|
|
13
|
-
- Non-maintainer users (this skill publishes
|
|
13
|
+
- Non-maintainer users (this skill publishes the package defined in `package.json` — confirm scope before running)
|
|
14
14
|
- Internal project releases — only for the `roll` CLI package itself
|
|
15
15
|
- Hotfixing code without a version bump (use `$roll-fix`)
|
|
16
16
|
- Generating user-facing release notes (use `$roll-.changelog`)
|
|
@@ -92,8 +92,8 @@ After publish, show:
|
|
|
92
92
|
```
|
|
93
93
|
✅ Released v{version}
|
|
94
94
|
🏷 Tag: v{version} pushed to origin
|
|
95
|
-
📦 npm published: @
|
|
96
|
-
🔗 https://www.npmjs.com/package
|
|
95
|
+
📦 npm published: {package_name}@{version} # package name read from package.json
|
|
96
|
+
🔗 https://www.npmjs.com/package/{package_name}
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
## Abort Conditions
|