sloptimize 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +8 -0
- package/.mcp.json +9 -0
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/bin/sloptimize.mjs +346 -0
- package/docs/DESIGN-mecharoyale-v0.md +32 -0
- package/docs/INTEGRATION.md +230 -0
- package/docs/JITTER-AND-FOOTPRINTS.md +236 -0
- package/docs/SPEC-attach.md +176 -0
- package/docs/SPEC.md +845 -0
- package/docs/USAGE.md +227 -0
- package/hooks/hooks.json +16 -0
- package/mcp/server.mjs +127 -0
- package/package.json +64 -0
- package/skills/install/SKILL.md +143 -0
- package/skills/sloptimize/SKILL.md +74 -0
- package/src/attach.mjs +197 -0
- package/src/census.js +193 -0
- package/src/classify.js +70 -0
- package/src/footprint.js +170 -0
- package/src/history.js +273 -0
- package/src/index.js +8 -0
- package/src/inject-body.js +152 -0
- package/src/motion.js +345 -0
- package/src/panel.js +530 -0
- package/src/proposals.mjs +268 -0
- package/src/recorder.js +235 -0
- package/src/watch.mjs +242 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sloptimize",
|
|
3
|
+
"owner": { "name": "m0dE" },
|
|
4
|
+
"plugins": [
|
|
5
|
+
{
|
|
6
|
+
"name": "sloptimize",
|
|
7
|
+
"source": "./",
|
|
8
|
+
"description": "The agent-native profiler for three.js games: always-on incident recording, zero-setup attach with file:line attribution, budgets with exit codes."
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sloptimize",
|
|
3
|
+
"displayName": "sloptimize",
|
|
4
|
+
"version": "0.3.0",
|
|
5
|
+
"description": "The agent-native profiler for browser games: always-on incident recording, zero-setup attach with file:line attribution, budgets with exit codes — the agent's senses and ruler for performance work.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": ["profiler", "three.js", "webgpu", "performance", "draw-calls"]
|
|
8
|
+
}
|
package/.mcp.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Moddio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# sloptimize
|
|
2
|
+
|
|
3
|
+
sloptimize optimizes your game's rendering performance by finding the
|
|
4
|
+
bottlenecks and reporting them to Claude Code to fix — all while you just play
|
|
5
|
+
the game. No action is required on your end.
|
|
6
|
+
|
|
7
|
+
**The agent-native profiler for browser games.** Your coding agent cannot
|
|
8
|
+
watch a game run — it will never feel a hitch, cannot screenshot 60 times a
|
|
9
|
+
second, and cannot verify a "fix" it cannot measure. sloptimize gives the
|
|
10
|
+
agent the three verbs it measurably lacks:
|
|
11
|
+
|
|
12
|
+
- **MEASURE** — an always-on flight recorder detects incidents (CPU spikes,
|
|
13
|
+
fps drops, GPU stalls, and the player's unit or camera SNAPPING off its own
|
|
14
|
+
trajectory) automatically, in the background, and writes them to disk
|
|
15
|
+
before anyone asks. The human just plays.
|
|
16
|
+
- **ATTRIBUTE** — every incident arrives classified with evidence
|
|
17
|
+
(`shader-compile: programs +2`, `long-script`, upload storms,
|
|
18
|
+
`snap 17.5m in one frame`), stamped with a **footprint** — the identity of
|
|
19
|
+
its cause and the game's situation (which machine, at the helm or on foot,
|
|
20
|
+
in combat…), never its time — so one cause across builds, sessions and
|
|
21
|
+
players is one issue with a count and a fix history, and — with the attach
|
|
22
|
+
tier — named by **function and file:line** from a rolling sampling profiler.
|
|
23
|
+
- **VERIFY** — exact counters (draw calls, triangles, pipelines — deterministic
|
|
24
|
+
on any renderer), perf budgets with exit codes, and honest labels: timing
|
|
25
|
+
numbers carry their regime (`hardware`/`software`) and are never compared
|
|
26
|
+
across them.
|
|
27
|
+
|
|
28
|
+
The division of labor is the design: **the tool decides what is true; the
|
|
29
|
+
agent decides what to try; the human plays.**
|
|
30
|
+
|
|
31
|
+
Proven in production on a 149k-line WebGPU battle-royale: the pipeline caught
|
|
32
|
+
a 205,000-calls/11s GPU upload storm from a player's real session, attributed
|
|
33
|
+
it, and verified the fix at >60× reduction — with the player doing nothing
|
|
34
|
+
but playing.
|
|
35
|
+
|
|
36
|
+
## The pipeline
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
game/browser ──► incidents (auto-detected, classified, clustered)
|
|
40
|
+
│
|
|
41
|
+
▼
|
|
42
|
+
.sloptimize/ ◄── the agent's reading room
|
|
43
|
+
profile.json rolling summary (median/p95/counters/regime)
|
|
44
|
+
perf.jsonl incident records, append-only, each with its footprint
|
|
45
|
+
clusters.json one cause = one cluster
|
|
46
|
+
census.json per-entity cost census (tier 1+)
|
|
47
|
+
fixes.jsonl the fix ledger: issue → solution, commit, MEASURED before/after
|
|
48
|
+
budgets.json YOUR limits (the one human-authored file)
|
|
49
|
+
│
|
|
50
|
+
┌───────────┴───────────┐
|
|
51
|
+
▼ ▼
|
|
52
|
+
Claude Code (agent) in-game debugger (human, OPTIONAL)
|
|
53
|
+
woken on new incidents Session · Issues · Optimizations · Settings —
|
|
54
|
+
(fp=<id> ×N on each); this tab's incidents + a note box; every cause
|
|
55
|
+
reads, fixes, verifies, grouped by footprint with ×N, last seen and the
|
|
56
|
+
records each fix fixes applied; p95/calls/hitches over time
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Showing the work is part of the loop: after a verified fix the agent runs
|
|
60
|
+
`sloptimize fix --title … --issue … --solution … --commit <sha>`, and the
|
|
61
|
+
record's before/after are two **measured** windows of the ledger (previous
|
|
62
|
+
build vs new build) — not numbers the agent typed. The debugger's Fixes tab
|
|
63
|
+
and `sloptimize history` read that ledger back.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm i -D sloptimize # in your game repo
|
|
69
|
+
# or one-off: npx sloptimize attach --launch http://localhost:3000
|
|
70
|
+
# or from a checkout: node sloptimize/bin/sloptimize.mjs … (bare Node, no install)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Zero dependencies, no postinstall, no supply chain — npm is delivery only.
|
|
74
|
+
|
|
75
|
+
## Quickest start: zero integration (tier 0)
|
|
76
|
+
|
|
77
|
+
Requires only Node 22+ and a Chromium. No game changes, no build changes:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx sloptimize attach --launch http://localhost:3000 --headless
|
|
81
|
+
# play / drive the game …then:
|
|
82
|
+
npx sloptimize report
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Attach connects over the Chrome DevTools Protocol, injects a recorder before
|
|
86
|
+
any page script (rAF timing, draw/triangle counts via graphics-API wraps,
|
|
87
|
+
pipeline creations WITH call stacks, upload bytes, GPU queue latency), and
|
|
88
|
+
runs a rolling sampling profiler so a freeze is attributed like:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
INCIDENT long-script|seededFreezeWork@game.js:512 — 900ms
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Limits, stated: Chromium-only; minified bundles attribute to minified names
|
|
95
|
+
unless you serve sourcemaps; entity-level attribution needs tier 1+.
|
|
96
|
+
|
|
97
|
+
## Higher fidelity: the in-page feed (tier 1)
|
|
98
|
+
|
|
99
|
+
One call per frame from wherever your loop already reads `renderer.info`:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
import { createRecorder } from 'sloptimize';
|
|
103
|
+
const rec = createRecorder({ budgetFrameMs: 16.7 });
|
|
104
|
+
// per frame:
|
|
105
|
+
rec.frame({ frameMs, insideRenderMs, calls, triangles, programs,
|
|
106
|
+
geometries, textures, spawned, paused });
|
|
107
|
+
// optional human channel (bind to a chord, e.g. Ctrl+F11):
|
|
108
|
+
rec.usermark({ windowMs: 5000, note, inputsHeld, world });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Ship the records to `.sloptimize/` however your stack likes — a vite host
|
|
112
|
+
gets a plugin (planned); any other host adds one dev-gated POST endpoint
|
|
113
|
+
(~100 lines; see `docs/INTEGRATION.md` for the reference implementation,
|
|
114
|
+
including the four traps that cost the first deployment real time:
|
|
115
|
+
**don't gate activation on hostname** (probe your dev endpoint instead),
|
|
116
|
+
**give the recorder its own rAF clock** (a game-loop-fed clock is blind to
|
|
117
|
+
boot/launch — exactly the windows you care about), **frameMs must bound
|
|
118
|
+
insideRenderMs**, and **never let the feed die silently** (retry the probe
|
|
119
|
+
and the posts on a backoff, buffer while dark, and SHOW the state — the
|
|
120
|
+
first deployment lost an hour of real freezes to a server restart that
|
|
121
|
+
dropped the ingest with no indication anywhere).
|
|
122
|
+
|
|
123
|
+
The wire contract the reference runtime keeps, so the files are useful on
|
|
124
|
+
their own:
|
|
125
|
+
- **every record is self-sufficient** — `build` (which bundle the tab runs)
|
|
126
|
+
and `phase` (menu/boot/launch/match…) ride each ledger line; hitches are
|
|
127
|
+
stamped at mint time, not post time;
|
|
128
|
+
- **a heartbeat record lands once a minute while armed**, so a quiet
|
|
129
|
+
`perf.jsonl` means "no session, or the feed is dark" — never just "idle"
|
|
130
|
+
(`sloptimize hook-status` warns when the ledger goes stale);
|
|
131
|
+
- **gpu-settle records** report how long a boot/reveal gate actually waited
|
|
132
|
+
on `onSubmittedWorkDone` — the on-hardware verification channel for
|
|
133
|
+
compile-stall fixes;
|
|
134
|
+
- **a hitch that overlapped pipeline/shader creates carries `createStacks`**
|
|
135
|
+
— the top 3 deduped `Error().stack` tails from the create wrappers (~2KB
|
|
136
|
+
cap), so a `programs +N` hitch from a machine you cannot profile names its
|
|
137
|
+
own call sites. The positions are minified (`bundle.js:L:C`); keep an
|
|
138
|
+
unreferenced sourcemap at build time and decode locally (the game repo's
|
|
139
|
+
`tools/decode-perf-stack.mjs` is a dependency-free reference decoder).
|
|
140
|
+
|
|
141
|
+
**Coordinate jitter and the issue catalogue** (tier 1, ~200 lines in the
|
|
142
|
+
game): feed the unit's and the camera's positions once per rendered frame
|
|
143
|
+
and a snap or oscillation lands as a classified `jitter` record; declare a
|
|
144
|
+
few facets of the player's situation and every incident of every kind is
|
|
145
|
+
footprinted, counted and linked to its fixes — the debugger's **Issues**
|
|
146
|
+
tab, `sloptimize issues`, and `fp=<id> ×N` on every wake line. The whole
|
|
147
|
+
recipe, with the three traps that make a naive position detector lie
|
|
148
|
+
(rotation, transient shakes, the sim's dt clamp), is
|
|
149
|
+
`docs/JITTER-AND-FOOTPRINTS.md`.
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
import { createMotionMonitor, canonicalContext, footprintOf } from 'sloptimize';
|
|
153
|
+
const motion = createMotionMonitor({ unit: 'm', longFrameMs: 50,
|
|
154
|
+
tracks: { unit: { floor: 0.1 }, camera: { floor: 0.1, reach: 'boom', follows: 'unit' } } });
|
|
155
|
+
// per rendered frame, after the render:
|
|
156
|
+
motion.sample('unit', pivot.x, pivot.y, pivot.z, now, { held: paused, phase, ctx });
|
|
157
|
+
motion.sample('camera', cam.x, cam.y, cam.z, now, { held: paused || lookInput, reach, phase, ctx });
|
|
158
|
+
// once a second: ctx = canonicalContext({ stance: 'helm', hull: 'elong-x', squad: 'duo', combat: 'no' });
|
|
159
|
+
// at post: for (const r of records) { r.ctx ??= ctx; const fp = footprintOf(r); if (fp) r.footprint = fp; }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Tier 2 (scene census, per-entity attribution, measured bisection) layers on
|
|
163
|
+
top where the engine grants scene access — see `docs/SPEC.md` §4.
|
|
164
|
+
|
|
165
|
+
## Claude Code integration — the whole point
|
|
166
|
+
|
|
167
|
+
This repo IS a Claude Code plugin. One install:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
claude --plugin-dir node_modules/sloptimize # after npm i -D sloptimize
|
|
171
|
+
claude --plugin-dir /path/to/sloptimize # from a checkout
|
|
172
|
+
# or via marketplace: /plugin marketplace add m0dE/sloptimize && /plugin install sloptimize
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Then let the agent wire your game: `/sloptimize:install` walks it through
|
|
176
|
+
the tier-1 integration (runtime, sink, budgets, hooks) and refuses to call
|
|
177
|
+
itself done until the feed is proven live end-to-end.
|
|
178
|
+
|
|
179
|
+
That carries three surfaces into every session:
|
|
180
|
+
- **Skill** — the doctrine: read → classify → census → ONE change → verify
|
|
181
|
+
with counters; never claim a perf fix without a measured before/after;
|
|
182
|
+
never quote timing from a software regime.
|
|
183
|
+
- **Prompt hook** — silent by default; when a NEW keyframe or budget breach
|
|
184
|
+
exists, up to five lines land in the agent's context on your next prompt.
|
|
185
|
+
- **MCP server** — `get_report`, `check_budgets`, `get_history`,
|
|
186
|
+
`get_issues` (the catalogue by footprint), `record_fix` (with the
|
|
187
|
+
footprints it addresses), and `attach_start` / `attach_stop` for the live
|
|
188
|
+
tier.
|
|
189
|
+
|
|
190
|
+
For instant wakeups (the agent starts fixing ~20s after the stutter, no
|
|
191
|
+
prompt needed), arm `sloptimize watch` as a session Monitor — one line, in
|
|
192
|
+
`docs/INTEGRATION.md` §5. Wire it into a `SessionStart` hook and every
|
|
193
|
+
session arms it by itself.
|
|
194
|
+
|
|
195
|
+
## Budgets: "fast enough" as an exit code
|
|
196
|
+
|
|
197
|
+
`.sloptimize/budgets.json` (the one file a human reviews):
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{ "perf.budget.draw_calls": 400, "perf.budget.frame_ms_p95": 16.7 }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npx sloptimize check # exit 0 inside · 1 breached · 4 unmeasured
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
That exit code is what lets an agent self-iterate in a loop that terminates.
|
|
208
|
+
|
|
209
|
+
## CLI
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
sloptimize report current profile + incidents + census hints
|
|
213
|
+
sloptimize check budgets → exit code (--counters-only for CI)
|
|
214
|
+
sloptimize census per-entity costs + closed-vocabulary hints
|
|
215
|
+
sloptimize history the timeline: p95 / draw calls / hitches per time
|
|
216
|
+
bucket and per build, plus the fix ledger
|
|
217
|
+
sloptimize fix record a verified fix (title, issue, solution,
|
|
218
|
+
commit) with MEASURED before/after windows
|
|
219
|
+
sloptimize attach tier-0: --launch <url> [--headless] [--port N]
|
|
220
|
+
sloptimize hook-status the prompt hook's ≤5-line ambient surface
|
|
221
|
+
sloptimize issues the catalogue: every incident grouped by FOOTPRINT
|
|
222
|
+
(cause + situation, never time) — how often, how
|
|
223
|
+
recently, which fixes were applied; --fp <id> for one
|
|
224
|
+
sloptimize watch the push channel: one stdout line per usermark /
|
|
225
|
+
≥100ms hitch / gpu cap-hit / coordinate jitter /
|
|
226
|
+
feed dark, each with fp=<id> ×N; never exits
|
|
227
|
+
sloptimize doctor what is wired, what is degraded, stated limits
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## What it will tell you it cannot do
|
|
231
|
+
|
|
232
|
+
Printed by `doctor`, kept in the spec, never silently degraded: no per-draw
|
|
233
|
+
GPU timing; bisection ranks rather than sums; workload repro, not trajectory
|
|
234
|
+
repro; timing from software renderers flagged and never compared; V8
|
|
235
|
+
inlining can split an incident cluster across an optimization boundary;
|
|
236
|
+
**correctness bugs are out of scope** — a profiler cannot find a logic bug,
|
|
237
|
+
and the doctrine routes "it looks/behaves wrong" reports away before anyone
|
|
238
|
+
burns a loop on them.
|
|
239
|
+
|
|
240
|
+
## Docs
|
|
241
|
+
|
|
242
|
+
- `docs/USAGE.md` — day-to-day use once wired: the operator's verbs, new-session pickup, multi-session semantics, monitoring options
|
|
243
|
+
- `docs/SPEC.md` — the founding specification (recorder, census, bench, anti-gaming posture)
|
|
244
|
+
- `docs/SPEC-attach.md` — v2: the incident pipeline, tier-0 attach, measured exit criteria
|
|
245
|
+
- `docs/INTEGRATION.md` — wiring a real game + Claude Code session, with the reference deployment's traps
|
|
246
|
+
- `docs/DESIGN-mecharoyale-v0.md` — the first field deployment's decision record
|
|
247
|
+
|
|
248
|
+
## Status
|
|
249
|
+
|
|
250
|
+
M0–M2 (recorder, census, budgets/CLI) and M-A0–A2 (attach, incident
|
|
251
|
+
identity, plugin packaging) shipped with measured exit criteria. Bench +
|
|
252
|
+
correctness gate (SPEC §6, M3) and paused-world bisection (M4) are next.
|
|
253
|
+
|
|
254
|
+
## Relationship to slopjs
|
|
255
|
+
|
|
256
|
+
A sibling on the same platform: slopjs is a pointing device for a
|
|
257
|
+
human-in-the-loop authoring session; sloptimize is a measurement loop that
|
|
258
|
+
works with nobody watching. Tier 2 consumes `@slopjs/inspector` primitives
|
|
259
|
+
(stable IDs, the coherent pause, snapshots) where present.
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT
|