Imported from julerex/tothemoon (
AGENTS.md). Install upstream withnpx skills add julerex/tothemoon. Copyright stays with the author.
AGENTS
Instructions for LLM agents working in this repository.
Git: commit and push to main (no pull requests)
Always commit and push directly to main when you finish a unit of work — after implementing a feature, fix, or other requested change that leaves a meaningful git diff. Do not leave completed work uncommitted unless the user explicitly says not to commit, or the change is only exploratory/scratch.
Do not open a pull request. Do not create a feature branch, draft PR, or review request unless the user explicitly asks for one. Land the change on main and push it.
When committing and pushing:
- Work on
main. If you are on another branch, check outmainand bring it up to date before committing. - Follow the usual safety rules: never update git config; never force-push; never skip hooks; never push secrets.
- Use a clear commit message (why the change exists, complete sentences).
- Prefer a single logical commit per finished task; push to
origin main(git push origin HEAD:mainorgit push origin main). - If the working tree is clean (nothing to commit), say so briefly and do not create an empty commit.
- After push, mention the commit hash (and that it was pushed to
main) in the final reply. Do not open or update a pull request.
Project notes
- Interactive Three.js mission theater: Starbase → low Earth orbit → translunar injection → lunar landing.
- Trajectory is baked at build time (
npm run precompute/npm run build). - Scene unit = 1 km. Prefer small, focused diffs over drive-by refactors. Keep non-test
src/files under ~400 LOC; extract along domain seams into prefixed siblings or subdirs and leave the old path as a re-export facade (see P3.16 indocs/NEXT.md). - Hygiene:
npm run typecheck,npm run lint(ESLint),npm test(ornpm run cifor all three). - Prefer JSDoc on exported pure helpers and module headers; extend unit tests when changing physics or timeline contracts.
- Do not verify in the browser unless the user asks. Tests and typecheck are the default. When they do ask, follow
docs/AGENT_BROWSER.md.
Cursor Cloud specific instructions
- Single frontend service; no backend/database/external services. Standard commands live in
package.jsonandREADME.md(npm run dev,npm run ci,npm run build, etc.). Node 22 is available and matches the toolchain. - The dev server runs at
http://localhost:5173/tothemoon/— the/tothemoon/base path is required; the bare roothttp://localhost:5173/will not load the app. - Starship / Super Heavy / Raptor and the Starbase pads (OLP-1 vs OLP-2, Mechazilla, production site):
docs/STARSHIP.md. - Official Starship Flight 13 page recap and fullscreen X-replay screenshot SOP (skip the broken spacex.com embed; use the direct broadcast URL):
docs/STARSHIP_13.md. Landing/splash highlight clips for later visual refinement: https://x.com/SpaceX/status/2082186658162626898 (same file). - Super Heavy V3 look-reference: Ijsz23 Sketchfab mesh, CC BY 4.0 —
assets/sketchfab/, credit indocs/STARSHIP.md.npm run measure-sketchfabwalks named nodes inscene.gltf. Do not loadscene.bin(gitignored, render-only, 3.8M tris). Runtime bells are procedural (makeBell,RAPTOR_BELL_RADIAL_SEGMENTS≥ 48) — a lathe stays round; CAD triangles on a cone do not. Sketchfab’s two primitives per Raptor are a 65 532-vert split of the whole engine, not bell vs powerhead. Booster-only download — it does not inform ship flaps / TPS. - Sketchfab / procedural-booster gotchas:
- Named nodes:
RAPTOR inner|middle|outer(3 / 10 / 20) andGridfin Y+|Y-|Z-. - Root is not booster-local.
Superheavy v3_36is translated (0, 50, 20) after Sketchfab’s axis conversion; world-spacehypot(x,z)reads ~20 m for every ring.measureSketchfabBoosterinverts that world matrix. Do not write a one-off walker that skips the invert. - Do not copy download meters onto
dimensions.ts. Outer ring 4.24 m + 0.65 m bell hangs outside the 9 m barrel. Flight 13 T+5:50 (assets/flight13-webcast/tplus-000550-split-gridfin-engines.jpg): outer lips on the skirt →BOOST_RING_OUTER3.85 m (BOOST_RING_OUTER + SL_BELL_R < R * 1.02). GRID_FIN_FROM_TOP_Mis meters.BOOST_H - 0.48was 19.2 m down the barrel (mesh-unit mixup). UsegridFinZ(). KeepGRID_FIN_AZIMUTHS[0] === π/2so gridfin-cam does not jump. KeepGRID_FIN_CHORD_M~0.9 (factory diamond lattice); Sketchfab’s 0.33 m chord is a sheet.- Eyeball: Flight 13 T− hold,
setCamera("engines")/"gridfin", HUD off (H). Webcast stills inassets/flight13-webcast/beat the CAD-like download when they disagree.
- Named nodes:
- Browser theater debug (Chrome DevTools MCP / CDP):
docs/AGENT_BROWSER.md. After a mission boots,window.__theater.snapshot()/.seek()/.setCamera()/.getCamera()/.setCameraPose()is the handle — do not scrape the HUD first. Force a document load when switching missions (?agent=<nonce>on the URL); a hash-only change will not reboot a live theater. CLI:scripts/theater-devtools.sh. Evaluate snippets:src/debug/cdpCommands.ts. - Gotchas found while verifying visuals in the live theater:
#theater-loadingcovers the canvas until Earth / Moon / star / pad JPEGs finish.__theater.readystays false until that overlay hides — wait for it before screenshots.- Mesh, material, and scene-graph edits need a full document load (
?agent=<nonce>). Vite HMR andseek()reuse the already-built craft; a hash-only change will not rebuild it. setCamera()/frameCamera()turn Auto-cam off. Free cameras (sun, earth, moon, booster, tower, chase) allow WASD / mouse; Fixed livestream mounts (aerial, ground1, tower1cam, tower2cam, starbase, trench, hull, engines, gridfin, fin, drone) reject movement (frameCamerais a no-op there).setCamera("gridfin")is the grid-fin mount, not booster-hull-cam.setCamera("booster")looks at Super Heavy from outside.setCamera("tower")looks at Mechazilla (WASD along the ground).setCamera("tower1cam")is Tower One Cam (OLP-1 peak looking at the stack).setCamera("tower2cam")is Tower Two Cam (OLP-2 peak looking at the stack).setCamera("chase")is Starship.setCamera("aerial")is the pad flying drone (T− hold wide).setCamera("ground1")is Ground Camera One (T−2 full stack and tower).setCamera("drone")is the sea-level recovery drone (post-splash orbit of the floating ship), not Ship chase. Webcast mounts (boosterHull,engines,flap) are applied by Auto-cam at shot times — press G to re-enable Auto-cam, thenseek()to the shot (e.g. Flight 13 T+4:16 for booster hull; T+1:05:26 for the drone).seek()writes the hash and can destroy the CDP execution context. After a seek that navigates, wait for__theater.readyagain (EVAL_WAIT_READY/afterFrame()). A?t=URL pauses.- Hide the HUD with H (
#hud.hud-hidden) before visual screenshots. T+0 pad is washed out by deluge steam — use T− hold for pad/tower, T+4:16 Auto-cam for booster hull, T+1:02:19 fin-cam for ship flaps.
- WebGL gotcha for manual/browser testing: the 3D theater needs a WebGL context, which the cloud VM lacks a GPU for. Modern Chrome (v140+) gates software rendering, so you must launch Chrome with
--enable-unsafe-swiftshader(together with--use-gl=angle --use-angle=swiftshader --ignore-gpu-blocklist) or the canvas stays black with aTHREE.WebGLRenderer: A WebGL context could not be createderror. The UI, routing, andwindow.__theaterstill work — only the 3D render fails. The Flight 13 X webcast does not need those flags. npm run build/npm run precomputeregenerate the committedsrc/data/*trajectory.jsonpacks; they usually produce a tiny diff (metadata) — revert those files unless a trajectory/physics change intentionally updates them.