@funpixel/tools 0.0.0-stage → 0.1.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/LICENSE +115 -0
- package/NOTICE +28 -0
- package/README.md +110 -2
- package/bench/LICENSE +674 -0
- package/bench/README.md +10 -0
- package/bench/entrypoint.py +94 -0
- package/bench/handlers/__init__.py +53 -0
- package/bench/handlers/apply_skin.py +192 -0
- package/bench/handlers/apply_texture.py +195 -0
- package/bench/handlers/audit_clips.py +351 -0
- package/bench/handlers/author_clips.py +297 -0
- package/bench/handlers/author_motion.py +298 -0
- package/bench/handlers/bake_emissive.py +136 -0
- package/bench/handlers/build_level.py +395 -0
- package/bench/handlers/decimate.py +220 -0
- package/bench/handlers/fit_rig.py +273 -0
- package/bench/handlers/fracture.py +233 -0
- package/bench/handlers/humanoid.py +119 -0
- package/bench/handlers/inspect.py +212 -0
- package/bench/handlers/level_check.py +68 -0
- package/bench/handlers/level_geo.py +150 -0
- package/bench/handlers/level_proof.py +117 -0
- package/bench/handlers/level_props.py +135 -0
- package/bench/handlers/lod.py +83 -0
- package/bench/handlers/merge_clips.py +122 -0
- package/bench/handlers/mirror_limb.py +336 -0
- package/bench/handlers/motion_specs.json +11 -0
- package/bench/handlers/normalize_materials.py +182 -0
- package/bench/handlers/normalize_transform.py +174 -0
- package/bench/handlers/prune_clips.py +141 -0
- package/bench/handlers/raster.py +98 -0
- package/bench/handlers/render.py +239 -0
- package/bench/handlers/render_views.py +101 -0
- package/bench/handlers/retarget_clips.py +185 -0
- package/bench/handlers/scene.py +311 -0
- package/bench/handlers/sheet.py +118 -0
- package/bench/handlers/texels.py +176 -0
- package/bench/handlers/views.py +232 -0
- package/dist/api/generated/openapi.js +1 -0
- package/dist/api/http.js +71 -0
- package/dist/api/jobs.js +94 -0
- package/dist/assets/bakeoff.js +61 -0
- package/dist/assets/counts.js +60 -0
- package/dist/assets/gen.js +230 -0
- package/dist/assets/lock.js +77 -0
- package/dist/assets/manifest.js +236 -0
- package/dist/assets/palette-fit.js +99 -0
- package/dist/assets/pixelart.js +192 -0
- package/dist/assets/pixels.js +131 -0
- package/dist/assets/plan.js +169 -0
- package/dist/assets/process.js +209 -0
- package/dist/assets/script.js +194 -0
- package/dist/assets/steps.js +294 -0
- package/dist/bin.js +8 -0
- package/dist/board/board.js +99 -0
- package/dist/cast/library.js +102 -0
- package/dist/cast/measure.js +222 -0
- package/dist/cli.js +2917 -0
- package/dist/clip/clip.js +83 -0
- package/dist/commands/account.js +158 -0
- package/dist/commands/assets-pixels.js +106 -0
- package/dist/commands/assets.js +473 -0
- package/dist/commands/beats.js +77 -0
- package/dist/commands/board.js +162 -0
- package/dist/commands/boxes.js +89 -0
- package/dist/commands/capture-all.js +92 -0
- package/dist/commands/cast.js +84 -0
- package/dist/commands/character.js +58 -0
- package/dist/commands/check.js +404 -0
- package/dist/commands/clip.js +112 -0
- package/dist/commands/compare-takes.js +204 -0
- package/dist/commands/compare-target.js +149 -0
- package/dist/commands/compare.js +117 -0
- package/dist/commands/compose-variations.js +202 -0
- package/dist/commands/compose.js +163 -0
- package/dist/commands/cut.js +13 -0
- package/dist/commands/decide.js +90 -0
- package/dist/commands/depth.js +171 -0
- package/dist/commands/dev.js +138 -0
- package/dist/commands/directions.js +106 -0
- package/dist/commands/doctor.js +281 -0
- package/dist/commands/export.js +88 -0
- package/dist/commands/feedback.js +496 -0
- package/dist/commands/feel.js +144 -0
- package/dist/commands/fidelity.js +95 -0
- package/dist/commands/find.js +116 -0
- package/dist/commands/firstlook.js +138 -0
- package/dist/commands/flow.js +600 -0
- package/dist/commands/framing.js +53 -0
- package/dist/commands/gdd.js +227 -0
- package/dist/commands/gen.js +512 -0
- package/dist/commands/greenlight.js +148 -0
- package/dist/commands/hook.js +171 -0
- package/dist/commands/init.js +234 -0
- package/dist/commands/inspect.js +161 -0
- package/dist/commands/job.js +41 -0
- package/dist/commands/jobs.js +87 -0
- package/dist/commands/journal.js +58 -0
- package/dist/commands/journey.js +228 -0
- package/dist/commands/judge.js +191 -0
- package/dist/commands/lanes.js +571 -0
- package/dist/commands/lessons.js +74 -0
- package/dist/commands/life.js +134 -0
- package/dist/commands/login.js +134 -0
- package/dist/commands/look-3d.js +105 -0
- package/dist/commands/look-fit.js +116 -0
- package/dist/commands/look-hud.js +180 -0
- package/dist/commands/look-read.js +162 -0
- package/dist/commands/look-sheet.js +152 -0
- package/dist/commands/look-theme.js +136 -0
- package/dist/commands/look.js +373 -0
- package/dist/commands/loop.js +112 -0
- package/dist/commands/loudness.js +69 -0
- package/dist/commands/mobile-qa.js +119 -0
- package/dist/commands/model.js +256 -0
- package/dist/commands/models.js +93 -0
- package/dist/commands/motion.js +305 -0
- package/dist/commands/next.js +72 -0
- package/dist/commands/note.js +294 -0
- package/dist/commands/optimize.js +212 -0
- package/dist/commands/owner.js +109 -0
- package/dist/commands/pack.js +66 -0
- package/dist/commands/parts.js +635 -0
- package/dist/commands/phone.js +398 -0
- package/dist/commands/pick.js +146 -0
- package/dist/commands/pitch.js +205 -0
- package/dist/commands/places.js +89 -0
- package/dist/commands/play.js +368 -0
- package/dist/commands/playable.js +51 -0
- package/dist/commands/playtest.js +262 -0
- package/dist/commands/profile.js +292 -0
- package/dist/commands/progress.js +537 -0
- package/dist/commands/publish.js +165 -0
- package/dist/commands/reach.js +72 -0
- package/dist/commands/reel.js +177 -0
- package/dist/commands/review.js +112 -0
- package/dist/commands/rooms.js +180 -0
- package/dist/commands/schema.js +29 -0
- package/dist/commands/score.js +326 -0
- package/dist/commands/serve.js +67 -0
- package/dist/commands/setup.js +254 -0
- package/dist/commands/sim.js +179 -0
- package/dist/commands/simulator.js +154 -0
- package/dist/commands/smoke-build.js +305 -0
- package/dist/commands/smoke.js +37 -0
- package/dist/commands/spec.js +114 -0
- package/dist/commands/store.js +154 -0
- package/dist/commands/test.js +104 -0
- package/dist/commands/trailer.js +255 -0
- package/dist/commands/unity-editor.js +322 -0
- package/dist/commands/unity-player.js +672 -0
- package/dist/commands/unity.js +198 -0
- package/dist/commands/upgrade.js +186 -0
- package/dist/commands/variety.js +108 -0
- package/dist/commands/verify.js +293 -0
- package/dist/commands/walk.js +449 -0
- package/dist/commands/widen.js +133 -0
- package/dist/commands/world.js +175 -0
- package/dist/commands/wow.js +143 -0
- package/dist/commands/zero.js +71 -0
- package/dist/compose/adsheet.js +61 -0
- package/dist/compose/contract.js +248 -0
- package/dist/compose/facts.js +81 -0
- package/dist/compose/hyperframes.js +102 -0
- package/dist/compose/page.js +216 -0
- package/dist/compose/probe.js +160 -0
- package/dist/compose/run.js +170 -0
- package/dist/compose/sheet.js +92 -0
- package/dist/compose/starters.js +247 -0
- package/dist/compose/variations.js +203 -0
- package/dist/criteria.js +35 -0
- package/dist/depth/experience.js +61 -0
- package/dist/depth/fill.js +102 -0
- package/dist/depth/inventory.js +161 -0
- package/dist/depth/pacing.js +173 -0
- package/dist/depth/story.js +97 -0
- package/dist/dev/devtools.js +119 -0
- package/dist/dev/hold.js +46 -0
- package/dist/dev/lan.js +15 -0
- package/dist/dev/open.js +30 -0
- package/dist/dev/qr.js +124 -0
- package/dist/devtools/LICENSES/pixi.js.LICENSE +21 -0
- package/dist/devtools/LICENSES/preact.LICENSE +21 -0
- package/dist/devtools/LICENSES/source-map-js.LICENSE +28 -0
- package/dist/devtools/LICENSES/three.LICENSE +21 -0
- package/dist/devtools/inspector/funpixel-inspector.pixi.min.js +1 -0
- package/dist/devtools/inspector/funpixel-inspector.threejs.min.js +1 -0
- package/dist/devtools/inspector/hooked-engines/pixi.min.js +8 -0
- package/dist/devtools/inspector/hooked-engines/preact.min.js +1 -0
- package/dist/devtools/inspector/hooked-engines/threejs.min.js +12 -0
- package/dist/devtools/kapture/funpixel-kapture.min.js +1 -0
- package/dist/devtools/profiler/funpixel-profiler.pixi.min.js +1 -0
- package/dist/devtools/profiler/funpixel-profiler.threejs.min.js +1 -0
- package/dist/direct/moves.js +234 -0
- package/dist/direct/read.js +59 -0
- package/dist/direct/round.js +80 -0
- package/dist/doctor/host.js +245 -0
- package/dist/doctor/unity-checks.js +380 -0
- package/dist/doctor/unity-history.js +589 -0
- package/dist/doctor/unity-host.js +160 -0
- package/dist/doctor/unity-import-rules.js +874 -0
- package/dist/doctor/unity-meta.js +225 -0
- package/dist/doctor/unity-project.js +297 -0
- package/dist/doctor/unity.js +171 -0
- package/dist/export/export.js +165 -0
- package/dist/export/metadata.js +25 -0
- package/dist/export/runtime.js +119 -0
- package/dist/export/scan.js +78 -0
- package/dist/export/select.js +193 -0
- package/dist/export/strip.js +42 -0
- package/dist/feedback/ab.js +55 -0
- package/dist/feedback/auto.js +44 -0
- package/dist/feedback/learn.js +255 -0
- package/dist/feedback/measure.js +49 -0
- package/dist/feedback/page.js +66 -0
- package/dist/feel/feedback.js +85 -0
- package/dist/feel/judge.js +420 -0
- package/dist/feel/run.js +185 -0
- package/dist/feel/spec.js +124 -0
- package/dist/feel/sweep.js +156 -0
- package/dist/fidelity/consistency.js +205 -0
- package/dist/fidelity/match.js +675 -0
- package/dist/fidelity/run.js +107 -0
- package/dist/find/keywords.js +97 -0
- package/dist/find/names.js +45 -0
- package/dist/find/route.js +59 -0
- package/dist/firstlook/quality.js +261 -0
- package/dist/firstlook/render.js +111 -0
- package/dist/firstlook/set.js +218 -0
- package/dist/flow/flow.js +455 -0
- package/dist/flow/look.js +139 -0
- package/dist/frame/behaviour.js +53 -0
- package/dist/frame/clock.js +117 -0
- package/dist/frame/contract.js +111 -0
- package/dist/frame/genre.js +65 -0
- package/dist/frame/idle.js +58 -0
- package/dist/frame/opening.js +171 -0
- package/dist/frames/loop.js +52 -0
- package/dist/frames/sheet.js +69 -0
- package/dist/gdd/book.js +483 -0
- package/dist/gdd/chapters.js +71 -0
- package/dist/gdd/design.js +135 -0
- package/dist/gdd/doc.js +32 -0
- package/dist/gdd/levelmap.js +428 -0
- package/dist/gdd/measures.js +477 -0
- package/dist/gdd/page.js +111 -0
- package/dist/gdd/rubric.js +250 -0
- package/dist/gdd/size.js +83 -0
- package/dist/gdd/tiers.js +148 -0
- package/dist/gdd/world.js +98 -0
- package/dist/gdd/worth.js +507 -0
- package/dist/gen/kinds.js +451 -0
- package/dist/gen/local/doctor.js +25 -0
- package/dist/gen/local/index.js +3 -0
- package/dist/gen/local/kokoro.js +334 -0
- package/dist/gen/local/master.js +248 -0
- package/dist/gen/local/provider.js +65 -0
- package/dist/gen/local/voice-rules.js +156 -0
- package/dist/gen/models.generated.js +469 -0
- package/dist/gen/models.js +53 -0
- package/dist/gen/prompt-lint.js +35 -0
- package/dist/gen/record.js +73 -0
- package/dist/gen/save.js +101 -0
- package/dist/gen/title.js +55 -0
- package/dist/gen/unity.js +334 -0
- package/dist/goals/goals.js +204 -0
- package/dist/goals/run.js +90 -0
- package/dist/greenlight/ambition.js +42 -0
- package/dist/greenlight/bible.js +138 -0
- package/dist/greenlight/lock.js +48 -0
- package/dist/greenlight/match.js +19 -0
- package/dist/greenlight/md.js +82 -0
- package/dist/greenlight/page.js +100 -0
- package/dist/greenlight/pitches.js +66 -0
- package/dist/greenlight/worth.js +90 -0
- package/dist/guidance.js +24 -0
- package/dist/init/bare.js +114 -0
- package/dist/init/git.js +56 -0
- package/dist/init/starter.js +166 -0
- package/dist/init/starters.js +179 -0
- package/dist/inspect/describe.js +125 -0
- package/dist/inspect/devserver.js +40 -0
- package/dist/journey/diff.js +74 -0
- package/dist/journey/from-round.js +44 -0
- package/dist/journey/record.js +76 -0
- package/dist/journey/run.js +139 -0
- package/dist/journey/script.js +223 -0
- package/dist/judge/common.js +60 -0
- package/dist/judge/identity.js +68 -0
- package/dist/judge/sources.js +37 -0
- package/dist/judge/structure.js +59 -0
- package/dist/keys/device.js +145 -0
- package/dist/keys/store.js +167 -0
- package/dist/lanes/fence.js +108 -0
- package/dist/lanes/gate.js +123 -0
- package/dist/lanes/lanes.js +355 -0
- package/dist/lanes/union.js +62 -0
- package/dist/lanes/where.js +49 -0
- package/dist/lessons/motifs.js +97 -0
- package/dist/lessons/store.js +56 -0
- package/dist/look/anim.js +206 -0
- package/dist/look/boxes.js +209 -0
- package/dist/look/dressing.js +24 -0
- package/dist/look/feedback.js +50 -0
- package/dist/look/fit.js +123 -0
- package/dist/look/held.js +92 -0
- package/dist/look/hook.js +169 -0
- package/dist/look/hud.js +189 -0
- package/dist/look/image-io.js +50 -0
- package/dist/look/life.js +96 -0
- package/dist/look/light.js +42 -0
- package/dist/look/look.js +85 -0
- package/dist/look/loops.js +108 -0
- package/dist/look/made-of.js +242 -0
- package/dist/look/moments.js +32 -0
- package/dist/look/motion.js +44 -0
- package/dist/look/phone-play.js +142 -0
- package/dist/look/player.js +32 -0
- package/dist/look/png.js +117 -0
- package/dist/look/readability.js +100 -0
- package/dist/look/roles.js +109 -0
- package/dist/look/sheet.js +104 -0
- package/dist/look/theme.js +278 -0
- package/dist/look/touch.js +129 -0
- package/dist/look/ui-style.js +102 -0
- package/dist/machine/procs.js +117 -0
- package/dist/managed.js +15 -0
- package/dist/mcp/hosted.js +174 -0
- package/dist/mcp/models.generated.js +538 -0
- package/dist/mcp/models.js +35 -0
- package/dist/mcp/server.js +330 -0
- package/dist/memory/decisions.js +138 -0
- package/dist/memory/journal.js +108 -0
- package/dist/model/bench.js +83 -0
- package/dist/model/blender.js +106 -0
- package/dist/model/clips.js +34 -0
- package/dist/model/finalize.js +33 -0
- package/dist/motion/checks.js +173 -0
- package/dist/motion/contract.js +177 -0
- package/dist/motion/encode.js +37 -0
- package/dist/motion/render.js +167 -0
- package/dist/motion/starter.js +54 -0
- package/dist/optimize/check.js +29 -0
- package/dist/optimize/helper.js +80 -0
- package/dist/owner/bar.js +285 -0
- package/dist/owner/match.js +105 -0
- package/dist/pack/build.js +97 -0
- package/dist/pack/deps.js +28 -0
- package/dist/pack/leaks.js +77 -0
- package/dist/pack/limits.js +51 -0
- package/dist/pack/report.js +35 -0
- package/dist/phone/baguette.js +139 -0
- package/dist/phone/listen.js +65 -0
- package/dist/pipeline/pipeline.js +929 -0
- package/dist/plan/actions.js +18 -0
- package/dist/plan/notes.js +15 -0
- package/dist/play/clock.js +40 -0
- package/dist/play/condition.js +45 -0
- package/dist/play/frames.js +60 -0
- package/dist/play/input.js +118 -0
- package/dist/play/pointer.js +21 -0
- package/dist/play/report.js +124 -0
- package/dist/play/server.js +274 -0
- package/dist/play/session.js +231 -0
- package/dist/play/steps.js +192 -0
- package/dist/playable/build.js +90 -0
- package/dist/playable/cta.js +88 -0
- package/dist/playable/networks.js +33 -0
- package/dist/playable/scan.js +46 -0
- package/dist/playtest/after-round.js +60 -0
- package/dist/playtest/assemble.js +168 -0
- package/dist/playtest/experience-run.js +69 -0
- package/dist/playtest/experience.js +77 -0
- package/dist/playtest/facts.js +90 -0
- package/dist/playtest/fidelity.js +179 -0
- package/dist/playtest/history.js +115 -0
- package/dist/playtest/hits.js +48 -0
- package/dist/playtest/hosted.js +98 -0
- package/dist/playtest/keep.js +110 -0
- package/dist/playtest/ledger.js +77 -0
- package/dist/playtest/length.js +113 -0
- package/dist/playtest/measure.js +181 -0
- package/dist/playtest/mobile-qa.js +391 -0
- package/dist/playtest/mockups.js +198 -0
- package/dist/playtest/moments.js +28 -0
- package/dist/playtest/prepare.js +162 -0
- package/dist/playtest/rank.js +184 -0
- package/dist/playtest/report.js +209 -0
- package/dist/playtest/route.js +139 -0
- package/dist/playtest/seats.js +128 -0
- package/dist/playtest/sheet.js +38 -0
- package/dist/playtest/stop.js +54 -0
- package/dist/playtest/stuck.js +47 -0
- package/dist/production/contract.js +61 -0
- package/dist/production/mirror.js +96 -0
- package/dist/production/tier.js +66 -0
- package/dist/profile/compare.js +171 -0
- package/dist/progress/answers.js +35 -0
- package/dist/progress/holds.js +106 -0
- package/dist/progress/notify.js +25 -0
- package/dist/progress/progress.js +300 -0
- package/dist/publish/build.js +54 -0
- package/dist/publish/folder.js +106 -0
- package/dist/publish/funpixel.js +96 -0
- package/dist/publish/hosts.js +48 -0
- package/dist/reach/envelope.js +111 -0
- package/dist/reach/load.js +84 -0
- package/dist/reach/side.js +320 -0
- package/dist/reach/spec3d.js +338 -0
- package/dist/reach/tiles.js +163 -0
- package/dist/reel/encode.js +34 -0
- package/dist/reel/tour.js +81 -0
- package/dist/relay/bots.js +62 -0
- package/dist/relay/bridge.js +54 -0
- package/dist/relay/local.js +82 -0
- package/dist/relay/redis.js +40 -0
- package/dist/review/audio.js +72 -0
- package/dist/review/ledger.js +60 -0
- package/dist/review/media.js +114 -0
- package/dist/review/model.js +348 -0
- package/dist/review/picture.js +113 -0
- package/dist/review/specs.js +134 -0
- package/dist/review/video.js +248 -0
- package/dist/schema/game.js +308 -0
- package/dist/score/comfort.js +79 -0
- package/dist/score/light.js +62 -0
- package/dist/score/measure.js +386 -0
- package/dist/score/vision.js +59 -0
- package/dist/serve/webgl.js +415 -0
- package/dist/setup/lsp.js +42 -0
- package/dist/sim/curve.js +122 -0
- package/dist/sim/judge.js +240 -0
- package/dist/sim/skeleton.js +65 -0
- package/dist/simulator/lease.js +159 -0
- package/dist/simulator/orient.js +57 -0
- package/dist/simulator/screen.js +113 -0
- package/dist/simulator/simctl.js +79 -0
- package/dist/smoke/art.js +20 -0
- package/dist/smoke/checks.js +165 -0
- package/dist/smoke/logic.js +73 -0
- package/dist/smoke/orphans.js +78 -0
- package/dist/smoke/palette.js +16 -0
- package/dist/smoke/sources.js +36 -0
- package/dist/sound/beats.js +95 -0
- package/dist/sound/loudness.js +94 -0
- package/dist/spec/extract.js +123 -0
- package/dist/spec/lint.js +261 -0
- package/dist/spec/presets.js +30 -0
- package/dist/spec/rooms.js +188 -0
- package/dist/spec/scaffold.js +457 -0
- package/dist/spec/sections.js +16 -0
- package/dist/spec/tunables.js +74 -0
- package/dist/stage/harnessignore.js +90 -0
- package/dist/stage/stage.js +57 -0
- package/dist/stage/tar.js +41 -0
- package/dist/status/board.js +172 -0
- package/dist/status/file.js +112 -0
- package/dist/status/inbox.js +55 -0
- package/dist/status/status.js +311 -0
- package/dist/store/answers.js +47 -0
- package/dist/store/doctor.js +120 -0
- package/dist/store/images.js +65 -0
- package/dist/store/init.js +101 -0
- package/dist/store/lint.js +392 -0
- package/dist/store/plan.js +133 -0
- package/dist/store/sdks.js +107 -0
- package/dist/studio/forks.js +93 -0
- package/dist/studio/pitch.js +294 -0
- package/dist/studio/widen.js +212 -0
- package/dist/suite/node-run.js +81 -0
- package/dist/suite/test-types.js +71 -0
- package/dist/trailer/assemble.js +90 -0
- package/dist/trailer/beats.js +74 -0
- package/dist/trailer/cards.js +77 -0
- package/dist/trailer/cues.js +78 -0
- package/dist/trailer/cut.js +49 -0
- package/dist/trailer/find.js +33 -0
- package/dist/trailer/mix.js +109 -0
- package/dist/trailer/placement.js +30 -0
- package/dist/types.js +1 -0
- package/dist/ui.js +79 -0
- package/dist/unity/bridge.js +251 -0
- package/dist/unity/disk.js +666 -0
- package/dist/unity/editor.js +471 -0
- package/dist/unity/gc.js +140 -0
- package/dist/unity/mcp-stdio.js +141 -0
- package/dist/unity/probe/budgets.js +84 -0
- package/dist/unity/probe/client.js +93 -0
- package/dist/unity/probe/install.js +118 -0
- package/dist/unity/probe/player.js +255 -0
- package/dist/unity/probe/report.js +102 -0
- package/dist/unity/probe/script.js +225 -0
- package/dist/unity/queue.js +353 -0
- package/dist/unity/run.js +172 -0
- package/dist/update.js +111 -0
- package/dist/variety/compare.js +104 -0
- package/dist/variety/places.js +109 -0
- package/dist/variety/profile.js +127 -0
- package/dist/vendor/verify/README.md +19 -0
- package/dist/vendor/verify/package.json +14 -0
- package/dist/vendor/verify/src/index.d.ts +105 -0
- package/dist/vendor/verify/src/index.js +78 -0
- package/dist/vendor/verify/src/init/preview_shim.js +63 -0
- package/dist/vendor/verify/src/page/actors.js +258 -0
- package/dist/vendor/verify/src/page/audio.js +106 -0
- package/dist/vendor/verify/src/page/audio_cues.js +141 -0
- package/dist/vendor/verify/src/page/audio_files.js +56 -0
- package/dist/vendor/verify/src/page/bot_sweep.js +72 -0
- package/dist/vendor/verify/src/page/capture_view.js +164 -0
- package/dist/vendor/verify/src/page/context_loss_listener.js +23 -0
- package/dist/vendor/verify/src/page/dressing.js +106 -0
- package/dist/vendor/verify/src/page/enter_view.js +33 -0
- package/dist/vendor/verify/src/page/feel.js +126 -0
- package/dist/vendor/verify/src/page/feel_sweep.js +75 -0
- package/dist/vendor/verify/src/page/first_paint.js +54 -0
- package/dist/vendor/verify/src/page/fonts.js +50 -0
- package/dist/vendor/verify/src/page/frame_honesty.js +44 -0
- package/dist/vendor/verify/src/page/front_door.js +197 -0
- package/dist/vendor/verify/src/page/grounding.js +448 -0
- package/dist/vendor/verify/src/page/hooks.js +74 -0
- package/dist/vendor/verify/src/page/hud.js +261 -0
- package/dist/vendor/verify/src/page/inventory.js +15 -0
- package/dist/vendor/verify/src/page/layout.js +378 -0
- package/dist/vendor/verify/src/page/levels.js +40 -0
- package/dist/vendor/verify/src/page/life.js +123 -0
- package/dist/vendor/verify/src/page/life_anim.js +501 -0
- package/dist/vendor/verify/src/page/made_of.js +83 -0
- package/dist/vendor/verify/src/page/mechanic_check.js +272 -0
- package/dist/vendor/verify/src/page/moments.js +36 -0
- package/dist/vendor/verify/src/page/motion.js +139 -0
- package/dist/vendor/verify/src/page/perf.js +255 -0
- package/dist/vendor/verify/src/page/phone_check.js +229 -0
- package/dist/vendor/verify/src/page/play.js +117 -0
- package/dist/vendor/verify/src/page/play_act.js +164 -0
- package/dist/vendor/verify/src/page/preview.js +33 -0
- package/dist/vendor/verify/src/page/recorder.js +54 -0
- package/dist/vendor/verify/src/page/render_health.js +83 -0
- package/dist/vendor/verify/src/page/renderer.js +75 -0
- package/dist/vendor/verify/src/page/scene.js +222 -0
- package/dist/vendor/verify/src/page/self_test.js +25 -0
- package/dist/vendor/verify/src/page/step_clock.js +247 -0
- package/dist/vendor/verify/src/page/surfaces.js +261 -0
- package/dist/vendor/verify/src/page/world_actors.js +264 -0
- package/dist/verify/browser.js +245 -0
- package/dist/verify/console.js +34 -0
- package/dist/verify/fingerprint.js +72 -0
- package/dist/verify/gpu.js +33 -0
- package/dist/verify/health.js +172 -0
- package/dist/verify/hooks.js +34 -0
- package/dist/verify/judge.js +303 -0
- package/dist/verify/players.js +123 -0
- package/dist/verify/probe.js +236 -0
- package/dist/verify/reads.js +32 -0
- package/dist/verify/run.js +56 -0
- package/dist/verify/serve.js +36 -0
- package/dist/walk/film.js +37 -0
- package/dist/walk/judge.js +287 -0
- package/dist/walk/routes.js +199 -0
- package/dist/walk/world.js +292 -0
- package/dist/world/brief.js +86 -0
- package/dist/world/check.js +254 -0
- package/dist/world/forge.js +542 -0
- package/dist/world/format.js +40 -0
- package/dist/world/variety.js +59 -0
- package/dist/wow/catalogue.js +86 -0
- package/dist/wow/picks.js +74 -0
- package/package.json +51 -4
- package/panel/asset-identity.md +61 -0
- package/panel/asset-structure.md +55 -0
- package/panel/blockers.md +47 -0
- package/panel/brief.md +89 -0
- package/panel/clip.md +54 -0
- package/panel/experience-personas.json +4 -0
- package/panel/experience.md +96 -0
- package/panel/fidelity-ui.md +74 -0
- package/panel/fidelity.md +83 -0
- package/panel/greenlight-pitch.md +69 -0
- package/panel/greenlight.md +87 -0
- package/panel/mobile-qa.md +128 -0
- package/panel/personas.json +6 -0
- package/panel/reference-match.md +12 -0
- package/panel/versus.md +31 -0
- package/plugins/.agents/plugins/marketplace.json +18 -0
- package/plugins/.claude-plugin/marketplace.json +17 -0
- package/plugins/.cursor-plugin/marketplace.json +13 -0
- package/plugins/LICENSE +115 -0
- package/plugins/README.md +16 -0
- package/plugins/THIRD_PARTY_NOTICES.md +75 -0
- package/plugins/claude/.claude-plugin/plugin.json +19 -0
- package/plugins/claude/.mcp.json +10 -0
- package/plugins/claude/LICENSE +115 -0
- package/plugins/claude/README.md +14 -0
- package/plugins/claude/THIRD_PARTY_NOTICES.md +75 -0
- package/plugins/claude/agents/art-director.md +54 -0
- package/plugins/claude/agents/audio-director.md +51 -0
- package/plugins/claude/agents/critic.md +113 -0
- package/plugins/claude/agents/lane.md +38 -0
- package/plugins/claude/agents/level-designer.md +45 -0
- package/plugins/claude/agents/mobile-qa.md +71 -0
- package/plugins/claude/agents/player.md +144 -0
- package/plugins/claude/agents/qa-lead.md +52 -0
- package/plugins/claude/commands/first-look.md +59 -0
- package/plugins/claude/commands/flow.md +50 -0
- package/plugins/claude/commands/lanes.md +38 -0
- package/plugins/claude/commands/mobile-qa.md +28 -0
- package/plugins/claude/commands/new.md +94 -0
- package/plugins/claude/commands/playtest.md +66 -0
- package/plugins/claude/commands/publish.md +23 -0
- package/plugins/claude/commands/smoke-build.md +25 -0
- package/plugins/claude/commands/verify.md +20 -0
- package/plugins/claude/hooks/brief.md +135 -0
- package/plugins/claude/hooks/critic-receipts.mjs +86 -0
- package/plugins/claude/hooks/durable.mjs +56 -0
- package/plugins/claude/hooks/fence.mjs +30 -0
- package/plugins/claude/hooks/floors.mjs +181 -0
- package/plugins/claude/hooks/flow.mjs +154 -0
- package/plugins/claude/hooks/front-door.mjs +68 -0
- package/plugins/claude/hooks/guidance.json +3 -0
- package/plugins/claude/hooks/hints.mjs +183 -0
- package/plugins/claude/hooks/hooks.json +62 -0
- package/plugins/claude/hooks/lib.mjs +362 -0
- package/plugins/claude/hooks/memory.mjs +92 -0
- package/plugins/claude/hooks/mobile-qa.mjs +43 -0
- package/plugins/claude/hooks/note.mjs +63 -0
- package/plugins/claude/hooks/nudge.mjs +98 -0
- package/plugins/claude/hooks/pipeline.mjs +207 -0
- package/plugins/claude/hooks/prompt.mjs +51 -0
- package/plugins/claude/hooks/quality.mjs +310 -0
- package/plugins/claude/hooks/reach.mjs +93 -0
- package/plugins/claude/hooks/scope.mjs +44 -0
- package/plugins/claude/hooks/session-log.mjs +101 -0
- package/plugins/claude/hooks/session-start.mjs +194 -0
- package/plugins/claude/hooks/skill-names.json +19 -0
- package/plugins/claude/hooks/stop-gate.mjs +303 -0
- package/plugins/claude/hooks/vendor/direct/round.mjs +81 -0
- package/plugins/claude/hooks/vendor/find/names.mjs +46 -0
- package/plugins/claude/hooks/vendor/firstlook/set.mjs +219 -0
- package/plugins/claude/hooks/vendor/flow/flow.mjs +456 -0
- package/plugins/claude/hooks/vendor/flow/look.mjs +140 -0
- package/plugins/claude/hooks/vendor/gdd/book.mjs +484 -0
- package/plugins/claude/hooks/vendor/gdd/chapters.mjs +72 -0
- package/plugins/claude/hooks/vendor/gdd/doc.mjs +33 -0
- package/plugins/claude/hooks/vendor/gdd/levelmap.mjs +429 -0
- package/plugins/claude/hooks/vendor/gdd/rubric.mjs +251 -0
- package/plugins/claude/hooks/vendor/gdd/worth.mjs +508 -0
- package/plugins/claude/hooks/vendor/lanes/fence.mjs +109 -0
- package/plugins/claude/hooks/vendor/lanes/lanes.mjs +356 -0
- package/plugins/claude/hooks/vendor/lanes/union.mjs +63 -0
- package/plugins/claude/hooks/vendor/lessons/motifs.mjs +98 -0
- package/plugins/claude/hooks/vendor/lessons/store.mjs +57 -0
- package/plugins/claude/hooks/vendor/look/made-of.mjs +243 -0
- package/plugins/claude/hooks/vendor/look/roles.mjs +110 -0
- package/plugins/claude/hooks/vendor/memory/decisions.mjs +139 -0
- package/plugins/claude/hooks/vendor/memory/journal.mjs +109 -0
- package/plugins/claude/hooks/vendor/owner/bar.mjs +286 -0
- package/plugins/claude/hooks/vendor/pipeline/pipeline.mjs +930 -0
- package/plugins/claude/hooks/vendor/playtest/keep.mjs +111 -0
- package/plugins/claude/hooks/vendor/playtest/stuck.mjs +48 -0
- package/plugins/claude/hooks/vendor/progress/holds.mjs +107 -0
- package/plugins/claude/hooks/vendor/progress/progress.mjs +301 -0
- package/plugins/claude/hooks/vendor/sim/judge.mjs +241 -0
- package/plugins/claude/hooks/vendor/spec/rooms.mjs +189 -0
- package/plugins/claude/hooks/vendor/spec/tunables.mjs +75 -0
- package/plugins/claude/hooks/vendor/stage/harnessignore.mjs +91 -0
- package/plugins/claude/hooks/vendor/stage/stage.mjs +58 -0
- package/plugins/claude/hooks/vendor/status/board.mjs +173 -0
- package/plugins/claude/hooks/vendor/status/file.mjs +113 -0
- package/plugins/claude/hooks/vendor/studio/forks.mjs +94 -0
- package/plugins/claude/hooks/vendor/studio/pitch.mjs +295 -0
- package/plugins/claude/hooks/vendor/verify/fingerprint.mjs +73 -0
- package/plugins/claude/hooks/vendor/verify/reads.mjs +33 -0
- package/plugins/claude/hooks/vendor/wow/picks.mjs +75 -0
- package/plugins/claude/skills/3d/SKILL.md +89 -0
- package/plugins/claude/skills/3d/references/bake-off.md +48 -0
- package/plugins/claude/skills/3d/references/bench.md +103 -0
- package/plugins/claude/skills/3d/references/budgets.md +48 -0
- package/plugins/claude/skills/3d/references/characters.md +67 -0
- package/plugins/claude/skills/3d/references/concept-image.md +47 -0
- package/plugins/claude/skills/3d/references/engine-targets.md +31 -0
- package/plugins/claude/skills/3d/references/hero-route.md +104 -0
- package/plugins/claude/skills/3d/references/level-spec.md +109 -0
- package/plugins/claude/skills/3d/references/levels.md +84 -0
- package/plugins/claude/skills/3d/references/light-and-places.md +141 -0
- package/plugins/claude/skills/3d/references/models.md +111 -0
- package/plugins/claude/skills/3d/references/painted-target.md +184 -0
- package/plugins/claude/skills/3d/references/reading.md +41 -0
- package/plugins/claude/skills/3d/references/skin.md +85 -0
- package/plugins/claude/skills/3d/references/skyboxes.md +65 -0
- package/plugins/claude/skills/3d/references/textures.md +71 -0
- package/plugins/claude/skills/3d/references/three-loading.md +199 -0
- package/plugins/claude/skills/3d/references/unity.md +78 -0
- package/plugins/claude/skills/art/SKILL.md +104 -0
- package/plugins/claude/skills/art/references/2d-assets.md +108 -0
- package/plugins/claude/skills/art/references/animation.md +126 -0
- package/plugins/claude/skills/art/references/cast.md +73 -0
- package/plugins/claude/skills/art/references/characters.md +49 -0
- package/plugins/claude/skills/art/references/cost.md +107 -0
- package/plugins/claude/skills/art/references/effects.md +85 -0
- package/plugins/claude/skills/art/references/frames.md +87 -0
- package/plugins/claude/skills/art/references/judging-frames.md +64 -0
- package/plugins/claude/skills/art/references/look.md +102 -0
- package/plugins/claude/skills/art/references/measure.md +119 -0
- package/plugins/claude/skills/art/references/pipeline.md +185 -0
- package/plugins/claude/skills/art/references/pixi.md +277 -0
- package/plugins/claude/skills/art/references/planes.md +27 -0
- package/plugins/claude/skills/art/references/prompts.md +171 -0
- package/plugins/claude/skills/art/references/sheets.md +141 -0
- package/plugins/claude/skills/art/references/style-file.md +39 -0
- package/plugins/claude/skills/art/references/three.md +232 -0
- package/plugins/claude/skills/art/references/ui.md +163 -0
- package/plugins/claude/skills/art/references/unity.md +78 -0
- package/plugins/claude/skills/art/references/wiring.md +41 -0
- package/plugins/claude/skills/audio/SKILL.md +92 -0
- package/plugins/claude/skills/audio/references/adaptive-music.md +204 -0
- package/plugins/claude/skills/audio/references/engine.md +78 -0
- package/plugins/claude/skills/audio/references/measure.md +101 -0
- package/plugins/claude/skills/audio/references/unity.md +74 -0
- package/plugins/claude/skills/feedback/SKILL.md +120 -0
- package/plugins/claude/skills/feedback/references/memory.md +99 -0
- package/plugins/claude/skills/game-feel/SKILL.md +104 -0
- package/plugins/claude/skills/game-feel/references/contact.md +110 -0
- package/plugins/claude/skills/game-feel/references/measure.md +125 -0
- package/plugins/claude/skills/game-feel/references/motion.md +134 -0
- package/plugins/claude/skills/game-feel/references/payoffs.md +35 -0
- package/plugins/claude/skills/game-feel/references/recipes.md +107 -0
- package/plugins/claude/skills/game-feel/references/unity.md +73 -0
- package/plugins/claude/skills/game-logic/SKILL.md +113 -0
- package/plugins/claude/skills/game-logic/references/balance.md +88 -0
- package/plugins/claude/skills/game-logic/references/content-at-scale.md +147 -0
- package/plugins/claude/skills/game-logic/references/depth.md +89 -0
- package/plugins/claude/skills/game-logic/references/escalation.md +41 -0
- package/plugins/claude/skills/game-logic/references/levels.md +61 -0
- package/plugins/claude/skills/game-logic/references/probes.md +122 -0
- package/plugins/claude/skills/game-logic/references/unity.md +108 -0
- package/plugins/claude/skills/game-spine/SKILL.md +118 -0
- package/plugins/claude/skills/game-spine/references/camera.md +94 -0
- package/plugins/claude/skills/game-spine/references/multiplayer.md +101 -0
- package/plugins/claude/skills/game-spine/references/patterns.md +103 -0
- package/plugins/claude/skills/game-spine/references/performance.md +96 -0
- package/plugins/claude/skills/game-spine/references/pixi.md +26 -0
- package/plugins/claude/skills/game-spine/references/replay.md +102 -0
- package/plugins/claude/skills/game-spine/references/room-api.md +63 -0
- package/plugins/claude/skills/game-spine/references/room-modes.md +40 -0
- package/plugins/claude/skills/game-spine/references/sdk.md +114 -0
- package/plugins/claude/skills/game-spine/references/three.md +62 -0
- package/plugins/claude/skills/gdd/SKILL.md +116 -0
- package/plugins/claude/skills/gdd/references/as-built.md +40 -0
- package/plugins/claude/skills/gdd/references/design-measures.md +24 -0
- package/plugins/claude/skills/gdd/references/design-note.md +88 -0
- package/plugins/claude/skills/gdd/references/experience.md +92 -0
- package/plugins/claude/skills/gdd/references/first-look.md +226 -0
- package/plugins/claude/skills/gdd/references/floors.md +62 -0
- package/plugins/claude/skills/gdd/references/frame.md +75 -0
- package/plugins/claude/skills/gdd/references/gameplay-contract.md +81 -0
- package/plugins/claude/skills/gdd/references/gdd-template.md +417 -0
- package/plugins/claude/skills/gdd/references/genre-card.md +109 -0
- package/plugins/claude/skills/gdd/references/genre-platformer.md +159 -0
- package/plugins/claude/skills/gdd/references/genre-puzzle.md +145 -0
- package/plugins/claude/skills/gdd/references/genre-racing.md +137 -0
- package/plugins/claude/skills/gdd/references/genre-rpg.md +125 -0
- package/plugins/claude/skills/gdd/references/genre-shooter.md +128 -0
- package/plugins/claude/skills/gdd/references/genre-simulation.md +112 -0
- package/plugins/claude/skills/gdd/references/genre-strategy.md +116 -0
- package/plugins/claude/skills/gdd/references/genre-survival.md +122 -0
- package/plugins/claude/skills/gdd/references/in-the-loop.md +106 -0
- package/plugins/claude/skills/gdd/references/invent-first.md +131 -0
- package/plugins/claude/skills/gdd/references/level-and-fun.md +112 -0
- package/plugins/claude/skills/gdd/references/pages.md +47 -0
- package/plugins/claude/skills/gdd/references/plan-at-size.md +140 -0
- package/plugins/claude/skills/gdd/references/production-tiers.md +117 -0
- package/plugins/claude/skills/gdd/references/sections.md +148 -0
- package/plugins/claude/skills/gdd/references/spec-folder.md +131 -0
- package/plugins/claude/skills/gdd/references/start.md +110 -0
- package/plugins/claude/skills/gdd/references/starters.md +175 -0
- package/plugins/claude/skills/gdd/references/the-path.md +133 -0
- package/plugins/claude/skills/gdd/references/track.md +29 -0
- package/plugins/claude/skills/gdd/references/world-and-identity.md +165 -0
- package/plugins/claude/skills/gdd/references/worth.md +74 -0
- package/plugins/claude/skills/mobile-ui/SKILL.md +107 -0
- package/plugins/claude/skills/mobile-ui/references/contrast.md +123 -0
- package/plugins/claude/skills/mobile-ui/references/hud-and-title.md +108 -0
- package/plugins/claude/skills/mobile-ui/references/hud-genres.md +124 -0
- package/plugins/claude/skills/mobile-ui/references/phone-check.md +156 -0
- package/plugins/claude/skills/mobile-ui/references/recipes.md +279 -0
- package/plugins/claude/skills/mobile-ui/references/restyle-ui.md +229 -0
- package/plugins/claude/skills/mobile-ui/references/unity.md +61 -0
- package/plugins/claude/skills/publishing/SKILL.md +109 -0
- package/plugins/claude/skills/publishing/references/compose.md +113 -0
- package/plugins/claude/skills/publishing/references/motion.md +120 -0
- package/plugins/claude/skills/publishing/references/networks.md +75 -0
- package/plugins/claude/skills/publishing/references/other-hosts.md +65 -0
- package/plugins/claude/skills/publishing/references/playable-ad.md +90 -0
- package/plugins/claude/skills/publishing/references/server-build.md +85 -0
- package/plugins/claude/skills/publishing/references/store-shots.md +85 -0
- package/plugins/claude/skills/publishing/references/trailer.md +55 -0
- package/plugins/claude/skills/publishing/references/what-ships.md +30 -0
- package/plugins/claude/skills/store/SKILL.md +81 -0
- package/plugins/claude/skills/store/references/app-store.md +73 -0
- package/plugins/claude/skills/store/references/google-play.md +141 -0
- package/plugins/claude/skills/store/references/ios-release.md +63 -0
- package/plugins/claude/skills/store/references/money.md +66 -0
- package/plugins/claude/skills/store/references/purchases-and-review.md +74 -0
- package/plugins/claude/skills/store/references/readiness-page.md +24 -0
- package/plugins/claude/skills/store/references/readiness.md +107 -0
- package/plugins/claude/skills/store/references/steam.md +38 -0
- package/plugins/claude/skills/store/references/unity-store.md +55 -0
- package/plugins/claude/skills/store/references/web-portals.md +43 -0
- package/plugins/claude/skills/unity-game/SKILL.md +103 -0
- package/plugins/claude/skills/unity-game/references/agent-ops.md +54 -0
- package/plugins/claude/skills/unity-game/references/fixes.md +40 -0
- package/plugins/claude/skills/unity-game/references/from-a-web-game.md +108 -0
- package/plugins/claude/skills/unity-game/references/handing-off.md +42 -0
- package/plugins/claude/skills/unity-game/references/performance.md +59 -0
- package/plugins/claude/skills/unity-game/references/probe.md +80 -0
- package/plugins/claude/skills/unity-game/references/shared-mac.md +121 -0
- package/plugins/claude/skills/unity-game/references/traps.md +108 -0
- package/plugins/claude/skills/verifying/SKILL.md +109 -0
- package/plugins/claude/skills/verifying/references/actors.md +94 -0
- package/plugins/claude/skills/verifying/references/bar.md +62 -0
- package/plugins/claude/skills/verifying/references/checks.md +69 -0
- package/plugins/claude/skills/verifying/references/experience.md +67 -0
- package/plugins/claude/skills/verifying/references/eyes.md +54 -0
- package/plugins/claude/skills/verifying/references/fidelity.md +126 -0
- package/plugins/claude/skills/verifying/references/hit-bar.md +31 -0
- package/plugins/claude/skills/verifying/references/journeys.md +77 -0
- package/plugins/claude/skills/verifying/references/mobile-qa.md +123 -0
- package/plugins/claude/skills/verifying/references/playtest.md +161 -0
- package/plugins/claude/skills/verifying/references/review.md +105 -0
- package/plugins/claude/skills/verifying/references/rubric.md +67 -0
- package/plugins/claude/skills/verifying/references/scenes.md +49 -0
- package/plugins/claude/skills/verifying/references/unity.md +60 -0
- package/plugins/claude/skills/world-and-story/SKILL.md +99 -0
- package/plugins/claude/skills/world-and-story/references/atmosphere.md +208 -0
- package/plugins/claude/skills/world-and-story/references/forged-world.md +79 -0
- package/plugins/claude/skills/world-and-story/references/living-world.md +237 -0
- package/plugins/claude/skills/world-and-story/references/opening-and-chapters.md +215 -0
- package/plugins/claude/skills/world-and-story/references/shelf.md +80 -0
- package/plugins/claude/skills/world-and-story/references/story-at-size.md +159 -0
- package/plugins/claude/skills/world-and-story/references/voice.md +175 -0
- package/plugins/claude/skills/world-and-story/references/world-kit.md +154 -0
- package/plugins/codex/.codex-plugin/plugin.json +27 -0
- package/plugins/codex/.mcp.json +7 -0
- package/plugins/codex/AGENTS.md +135 -0
- package/plugins/codex/LICENSE +115 -0
- package/plugins/codex/README.md +17 -0
- package/plugins/codex/THIRD_PARTY_NOTICES.md +75 -0
- package/plugins/codex/hooks/brief.md +135 -0
- package/plugins/codex/hooks/codex-critic.mjs +15 -0
- package/plugins/codex/hooks/critic-receipts.mjs +86 -0
- package/plugins/codex/hooks/durable.mjs +56 -0
- package/plugins/codex/hooks/fence.mjs +30 -0
- package/plugins/codex/hooks/floors.mjs +181 -0
- package/plugins/codex/hooks/flow.mjs +154 -0
- package/plugins/codex/hooks/front-door.mjs +68 -0
- package/plugins/codex/hooks/guidance.json +3 -0
- package/plugins/codex/hooks/hints.mjs +183 -0
- package/plugins/codex/hooks/hooks.json +59 -0
- package/plugins/codex/hooks/host.md +54 -0
- package/plugins/codex/hooks/lib.mjs +362 -0
- package/plugins/codex/hooks/memory.mjs +92 -0
- package/plugins/codex/hooks/mobile-qa.mjs +43 -0
- package/plugins/codex/hooks/note.mjs +63 -0
- package/plugins/codex/hooks/nudge.mjs +98 -0
- package/plugins/codex/hooks/pipeline.mjs +207 -0
- package/plugins/codex/hooks/prompt.mjs +51 -0
- package/plugins/codex/hooks/quality.mjs +310 -0
- package/plugins/codex/hooks/reach.mjs +93 -0
- package/plugins/codex/hooks/scope.mjs +44 -0
- package/plugins/codex/hooks/session-log.mjs +101 -0
- package/plugins/codex/hooks/session-start.mjs +194 -0
- package/plugins/codex/hooks/stop-gate.mjs +303 -0
- package/plugins/codex/hooks/vendor/direct/round.mjs +81 -0
- package/plugins/codex/hooks/vendor/find/names.mjs +46 -0
- package/plugins/codex/hooks/vendor/firstlook/set.mjs +219 -0
- package/plugins/codex/hooks/vendor/flow/flow.mjs +456 -0
- package/plugins/codex/hooks/vendor/flow/look.mjs +140 -0
- package/plugins/codex/hooks/vendor/gdd/book.mjs +484 -0
- package/plugins/codex/hooks/vendor/gdd/chapters.mjs +72 -0
- package/plugins/codex/hooks/vendor/gdd/doc.mjs +33 -0
- package/plugins/codex/hooks/vendor/gdd/levelmap.mjs +429 -0
- package/plugins/codex/hooks/vendor/gdd/rubric.mjs +251 -0
- package/plugins/codex/hooks/vendor/gdd/worth.mjs +508 -0
- package/plugins/codex/hooks/vendor/lanes/fence.mjs +109 -0
- package/plugins/codex/hooks/vendor/lanes/lanes.mjs +356 -0
- package/plugins/codex/hooks/vendor/lanes/union.mjs +63 -0
- package/plugins/codex/hooks/vendor/lessons/motifs.mjs +98 -0
- package/plugins/codex/hooks/vendor/lessons/store.mjs +57 -0
- package/plugins/codex/hooks/vendor/look/made-of.mjs +243 -0
- package/plugins/codex/hooks/vendor/look/roles.mjs +110 -0
- package/plugins/codex/hooks/vendor/memory/decisions.mjs +139 -0
- package/plugins/codex/hooks/vendor/memory/journal.mjs +109 -0
- package/plugins/codex/hooks/vendor/owner/bar.mjs +286 -0
- package/plugins/codex/hooks/vendor/pipeline/pipeline.mjs +930 -0
- package/plugins/codex/hooks/vendor/playtest/keep.mjs +111 -0
- package/plugins/codex/hooks/vendor/playtest/stuck.mjs +48 -0
- package/plugins/codex/hooks/vendor/progress/holds.mjs +107 -0
- package/plugins/codex/hooks/vendor/progress/progress.mjs +301 -0
- package/plugins/codex/hooks/vendor/sim/judge.mjs +241 -0
- package/plugins/codex/hooks/vendor/spec/rooms.mjs +189 -0
- package/plugins/codex/hooks/vendor/spec/tunables.mjs +75 -0
- package/plugins/codex/hooks/vendor/stage/harnessignore.mjs +91 -0
- package/plugins/codex/hooks/vendor/stage/stage.mjs +58 -0
- package/plugins/codex/hooks/vendor/status/board.mjs +173 -0
- package/plugins/codex/hooks/vendor/status/file.mjs +113 -0
- package/plugins/codex/hooks/vendor/studio/forks.mjs +94 -0
- package/plugins/codex/hooks/vendor/studio/pitch.mjs +295 -0
- package/plugins/codex/hooks/vendor/verify/fingerprint.mjs +73 -0
- package/plugins/codex/hooks/vendor/verify/reads.mjs +33 -0
- package/plugins/codex/hooks/vendor/wow/picks.mjs +75 -0
- package/plugins/codex/mcp.json +9 -0
- package/plugins/codex/plugin.json +26 -0
- package/plugins/codex/skills/fp-art-director/SKILL.md +58 -0
- package/plugins/codex/skills/fp-art-director/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-art-director/references/codex.md +54 -0
- package/plugins/codex/skills/fp-audio-director/SKILL.md +55 -0
- package/plugins/codex/skills/fp-audio-director/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-audio-director/references/codex.md +54 -0
- package/plugins/codex/skills/fp-critic/SKILL.md +117 -0
- package/plugins/codex/skills/fp-critic/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-critic/references/codex.md +54 -0
- package/plugins/codex/skills/fp-first-look/SKILL.md +59 -0
- package/plugins/codex/skills/fp-first-look/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-flow/SKILL.md +50 -0
- package/plugins/codex/skills/fp-flow/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-lane/SKILL.md +41 -0
- package/plugins/codex/skills/fp-lane/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-lane/references/codex.md +54 -0
- package/plugins/codex/skills/fp-lanes/SKILL.md +38 -0
- package/plugins/codex/skills/fp-lanes/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-level-designer/SKILL.md +49 -0
- package/plugins/codex/skills/fp-level-designer/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-level-designer/references/codex.md +54 -0
- package/plugins/codex/skills/fp-mobile-qa/SKILL.md +28 -0
- package/plugins/codex/skills/fp-mobile-qa/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-mobile-qa-seat/SKILL.md +74 -0
- package/plugins/codex/skills/fp-mobile-qa-seat/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-mobile-qa-seat/references/codex.md +54 -0
- package/plugins/codex/skills/fp-new/SKILL.md +94 -0
- package/plugins/codex/skills/fp-new/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-player/SKILL.md +147 -0
- package/plugins/codex/skills/fp-player/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-player/references/codex.md +54 -0
- package/plugins/codex/skills/fp-playtest/SKILL.md +66 -0
- package/plugins/codex/skills/fp-playtest/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-publish/SKILL.md +23 -0
- package/plugins/codex/skills/fp-publish/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-qa-lead/SKILL.md +56 -0
- package/plugins/codex/skills/fp-qa-lead/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-qa-lead/references/codex.md +54 -0
- package/plugins/codex/skills/fp-smoke-build/SKILL.md +25 -0
- package/plugins/codex/skills/fp-smoke-build/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-studio/SKILL.md +59 -0
- package/plugins/codex/skills/fp-studio/agents/openai.yaml +12 -0
- package/plugins/codex/skills/fp-verify/SKILL.md +20 -0
- package/plugins/codex/skills/fp-verify/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-3d/SKILL.md +89 -0
- package/plugins/codex/skills/funpixel-3d/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-3d/references/bake-off.md +48 -0
- package/plugins/codex/skills/funpixel-3d/references/bench.md +103 -0
- package/plugins/codex/skills/funpixel-3d/references/budgets.md +48 -0
- package/plugins/codex/skills/funpixel-3d/references/characters.md +67 -0
- package/plugins/codex/skills/funpixel-3d/references/concept-image.md +47 -0
- package/plugins/codex/skills/funpixel-3d/references/engine-targets.md +31 -0
- package/plugins/codex/skills/funpixel-3d/references/hero-route.md +104 -0
- package/plugins/codex/skills/funpixel-3d/references/level-spec.md +109 -0
- package/plugins/codex/skills/funpixel-3d/references/levels.md +84 -0
- package/plugins/codex/skills/funpixel-3d/references/light-and-places.md +141 -0
- package/plugins/codex/skills/funpixel-3d/references/models.md +111 -0
- package/plugins/codex/skills/funpixel-3d/references/painted-target.md +184 -0
- package/plugins/codex/skills/funpixel-3d/references/reading.md +41 -0
- package/plugins/codex/skills/funpixel-3d/references/skin.md +85 -0
- package/plugins/codex/skills/funpixel-3d/references/skyboxes.md +65 -0
- package/plugins/codex/skills/funpixel-3d/references/textures.md +71 -0
- package/plugins/codex/skills/funpixel-3d/references/three-loading.md +199 -0
- package/plugins/codex/skills/funpixel-3d/references/unity.md +78 -0
- package/plugins/codex/skills/funpixel-art/SKILL.md +104 -0
- package/plugins/codex/skills/funpixel-art/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-art/references/2d-assets.md +108 -0
- package/plugins/codex/skills/funpixel-art/references/animation.md +126 -0
- package/plugins/codex/skills/funpixel-art/references/cast.md +73 -0
- package/plugins/codex/skills/funpixel-art/references/characters.md +49 -0
- package/plugins/codex/skills/funpixel-art/references/cost.md +107 -0
- package/plugins/codex/skills/funpixel-art/references/effects.md +85 -0
- package/plugins/codex/skills/funpixel-art/references/frames.md +87 -0
- package/plugins/codex/skills/funpixel-art/references/judging-frames.md +64 -0
- package/plugins/codex/skills/funpixel-art/references/look.md +102 -0
- package/plugins/codex/skills/funpixel-art/references/measure.md +119 -0
- package/plugins/codex/skills/funpixel-art/references/pipeline.md +185 -0
- package/plugins/codex/skills/funpixel-art/references/pixi.md +277 -0
- package/plugins/codex/skills/funpixel-art/references/planes.md +27 -0
- package/plugins/codex/skills/funpixel-art/references/prompts.md +171 -0
- package/plugins/codex/skills/funpixel-art/references/sheets.md +141 -0
- package/plugins/codex/skills/funpixel-art/references/style-file.md +39 -0
- package/plugins/codex/skills/funpixel-art/references/three.md +232 -0
- package/plugins/codex/skills/funpixel-art/references/ui.md +163 -0
- package/plugins/codex/skills/funpixel-art/references/unity.md +78 -0
- package/plugins/codex/skills/funpixel-art/references/wiring.md +41 -0
- package/plugins/codex/skills/funpixel-audio/SKILL.md +92 -0
- package/plugins/codex/skills/funpixel-audio/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-audio/references/adaptive-music.md +204 -0
- package/plugins/codex/skills/funpixel-audio/references/engine.md +78 -0
- package/plugins/codex/skills/funpixel-audio/references/measure.md +101 -0
- package/plugins/codex/skills/funpixel-audio/references/unity.md +74 -0
- package/plugins/codex/skills/funpixel-feedback/SKILL.md +120 -0
- package/plugins/codex/skills/funpixel-feedback/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-feedback/references/memory.md +99 -0
- package/plugins/codex/skills/funpixel-game-feel/SKILL.md +104 -0
- package/plugins/codex/skills/funpixel-game-feel/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-game-feel/references/contact.md +110 -0
- package/plugins/codex/skills/funpixel-game-feel/references/measure.md +125 -0
- package/plugins/codex/skills/funpixel-game-feel/references/motion.md +134 -0
- package/plugins/codex/skills/funpixel-game-feel/references/payoffs.md +35 -0
- package/plugins/codex/skills/funpixel-game-feel/references/recipes.md +107 -0
- package/plugins/codex/skills/funpixel-game-feel/references/unity.md +73 -0
- package/plugins/codex/skills/funpixel-game-logic/SKILL.md +113 -0
- package/plugins/codex/skills/funpixel-game-logic/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-game-logic/references/balance.md +88 -0
- package/plugins/codex/skills/funpixel-game-logic/references/content-at-scale.md +147 -0
- package/plugins/codex/skills/funpixel-game-logic/references/depth.md +89 -0
- package/plugins/codex/skills/funpixel-game-logic/references/escalation.md +41 -0
- package/plugins/codex/skills/funpixel-game-logic/references/levels.md +61 -0
- package/plugins/codex/skills/funpixel-game-logic/references/probes.md +122 -0
- package/plugins/codex/skills/funpixel-game-logic/references/unity.md +108 -0
- package/plugins/codex/skills/funpixel-game-spine/SKILL.md +118 -0
- package/plugins/codex/skills/funpixel-game-spine/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-game-spine/references/camera.md +94 -0
- package/plugins/codex/skills/funpixel-game-spine/references/multiplayer.md +101 -0
- package/plugins/codex/skills/funpixel-game-spine/references/patterns.md +103 -0
- package/plugins/codex/skills/funpixel-game-spine/references/performance.md +96 -0
- package/plugins/codex/skills/funpixel-game-spine/references/pixi.md +26 -0
- package/plugins/codex/skills/funpixel-game-spine/references/replay.md +102 -0
- package/plugins/codex/skills/funpixel-game-spine/references/room-api.md +63 -0
- package/plugins/codex/skills/funpixel-game-spine/references/room-modes.md +40 -0
- package/plugins/codex/skills/funpixel-game-spine/references/sdk.md +114 -0
- package/plugins/codex/skills/funpixel-game-spine/references/three.md +62 -0
- package/plugins/codex/skills/funpixel-gdd/SKILL.md +116 -0
- package/plugins/codex/skills/funpixel-gdd/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-gdd/references/as-built.md +40 -0
- package/plugins/codex/skills/funpixel-gdd/references/design-measures.md +24 -0
- package/plugins/codex/skills/funpixel-gdd/references/design-note.md +88 -0
- package/plugins/codex/skills/funpixel-gdd/references/experience.md +92 -0
- package/plugins/codex/skills/funpixel-gdd/references/first-look.md +226 -0
- package/plugins/codex/skills/funpixel-gdd/references/floors.md +62 -0
- package/plugins/codex/skills/funpixel-gdd/references/frame.md +75 -0
- package/plugins/codex/skills/funpixel-gdd/references/gameplay-contract.md +81 -0
- package/plugins/codex/skills/funpixel-gdd/references/gdd-template.md +417 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-card.md +109 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-platformer.md +159 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-puzzle.md +145 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-racing.md +137 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-rpg.md +125 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-shooter.md +128 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-simulation.md +112 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-strategy.md +116 -0
- package/plugins/codex/skills/funpixel-gdd/references/genre-survival.md +122 -0
- package/plugins/codex/skills/funpixel-gdd/references/in-the-loop.md +106 -0
- package/plugins/codex/skills/funpixel-gdd/references/invent-first.md +131 -0
- package/plugins/codex/skills/funpixel-gdd/references/level-and-fun.md +112 -0
- package/plugins/codex/skills/funpixel-gdd/references/pages.md +47 -0
- package/plugins/codex/skills/funpixel-gdd/references/plan-at-size.md +140 -0
- package/plugins/codex/skills/funpixel-gdd/references/production-tiers.md +117 -0
- package/plugins/codex/skills/funpixel-gdd/references/sections.md +148 -0
- package/plugins/codex/skills/funpixel-gdd/references/spec-folder.md +131 -0
- package/plugins/codex/skills/funpixel-gdd/references/start.md +110 -0
- package/plugins/codex/skills/funpixel-gdd/references/starters.md +175 -0
- package/plugins/codex/skills/funpixel-gdd/references/the-path.md +133 -0
- package/plugins/codex/skills/funpixel-gdd/references/track.md +29 -0
- package/plugins/codex/skills/funpixel-gdd/references/world-and-identity.md +165 -0
- package/plugins/codex/skills/funpixel-gdd/references/worth.md +74 -0
- package/plugins/codex/skills/funpixel-mobile-ui/SKILL.md +107 -0
- package/plugins/codex/skills/funpixel-mobile-ui/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-mobile-ui/references/contrast.md +123 -0
- package/plugins/codex/skills/funpixel-mobile-ui/references/hud-and-title.md +108 -0
- package/plugins/codex/skills/funpixel-mobile-ui/references/hud-genres.md +124 -0
- package/plugins/codex/skills/funpixel-mobile-ui/references/phone-check.md +156 -0
- package/plugins/codex/skills/funpixel-mobile-ui/references/recipes.md +279 -0
- package/plugins/codex/skills/funpixel-mobile-ui/references/restyle-ui.md +229 -0
- package/plugins/codex/skills/funpixel-mobile-ui/references/unity.md +61 -0
- package/plugins/codex/skills/funpixel-publish/SKILL.md +109 -0
- package/plugins/codex/skills/funpixel-publish/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-publish/references/compose.md +113 -0
- package/plugins/codex/skills/funpixel-publish/references/motion.md +120 -0
- package/plugins/codex/skills/funpixel-publish/references/networks.md +75 -0
- package/plugins/codex/skills/funpixel-publish/references/other-hosts.md +65 -0
- package/plugins/codex/skills/funpixel-publish/references/playable-ad.md +90 -0
- package/plugins/codex/skills/funpixel-publish/references/server-build.md +85 -0
- package/plugins/codex/skills/funpixel-publish/references/store-shots.md +85 -0
- package/plugins/codex/skills/funpixel-publish/references/trailer.md +55 -0
- package/plugins/codex/skills/funpixel-publish/references/what-ships.md +30 -0
- package/plugins/codex/skills/funpixel-store/SKILL.md +81 -0
- package/plugins/codex/skills/funpixel-store/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-store/references/app-store.md +73 -0
- package/plugins/codex/skills/funpixel-store/references/google-play.md +141 -0
- package/plugins/codex/skills/funpixel-store/references/ios-release.md +63 -0
- package/plugins/codex/skills/funpixel-store/references/money.md +66 -0
- package/plugins/codex/skills/funpixel-store/references/purchases-and-review.md +74 -0
- package/plugins/codex/skills/funpixel-store/references/readiness-page.md +24 -0
- package/plugins/codex/skills/funpixel-store/references/readiness.md +107 -0
- package/plugins/codex/skills/funpixel-store/references/steam.md +38 -0
- package/plugins/codex/skills/funpixel-store/references/unity-store.md +55 -0
- package/plugins/codex/skills/funpixel-store/references/web-portals.md +43 -0
- package/plugins/codex/skills/funpixel-unity-game/SKILL.md +103 -0
- package/plugins/codex/skills/funpixel-unity-game/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-unity-game/references/agent-ops.md +54 -0
- package/plugins/codex/skills/funpixel-unity-game/references/fixes.md +40 -0
- package/plugins/codex/skills/funpixel-unity-game/references/from-a-web-game.md +108 -0
- package/plugins/codex/skills/funpixel-unity-game/references/handing-off.md +42 -0
- package/plugins/codex/skills/funpixel-unity-game/references/performance.md +59 -0
- package/plugins/codex/skills/funpixel-unity-game/references/probe.md +80 -0
- package/plugins/codex/skills/funpixel-unity-game/references/shared-mac.md +121 -0
- package/plugins/codex/skills/funpixel-unity-game/references/traps.md +108 -0
- package/plugins/codex/skills/funpixel-verify/SKILL.md +109 -0
- package/plugins/codex/skills/funpixel-verify/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-verify/references/actors.md +94 -0
- package/plugins/codex/skills/funpixel-verify/references/bar.md +62 -0
- package/plugins/codex/skills/funpixel-verify/references/checks.md +69 -0
- package/plugins/codex/skills/funpixel-verify/references/experience.md +67 -0
- package/plugins/codex/skills/funpixel-verify/references/eyes.md +54 -0
- package/plugins/codex/skills/funpixel-verify/references/fidelity.md +126 -0
- package/plugins/codex/skills/funpixel-verify/references/hit-bar.md +31 -0
- package/plugins/codex/skills/funpixel-verify/references/journeys.md +77 -0
- package/plugins/codex/skills/funpixel-verify/references/mobile-qa.md +123 -0
- package/plugins/codex/skills/funpixel-verify/references/playtest.md +161 -0
- package/plugins/codex/skills/funpixel-verify/references/review.md +105 -0
- package/plugins/codex/skills/funpixel-verify/references/rubric.md +67 -0
- package/plugins/codex/skills/funpixel-verify/references/scenes.md +49 -0
- package/plugins/codex/skills/funpixel-verify/references/unity.md +60 -0
- package/plugins/codex/skills/funpixel-world-and-story/SKILL.md +99 -0
- package/plugins/codex/skills/funpixel-world-and-story/agents/openai.yaml +12 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/atmosphere.md +208 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/forged-world.md +79 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/living-world.md +237 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/opening-and-chapters.md +215 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/shelf.md +80 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/story-at-size.md +159 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/voice.md +175 -0
- package/plugins/codex/skills/funpixel-world-and-story/references/world-kit.md +154 -0
- package/plugins/cursor/.cursor-plugin/plugin.json +26 -0
- package/plugins/cursor/LICENSE +115 -0
- package/plugins/cursor/README.md +13 -0
- package/plugins/cursor/THIRD_PARTY_NOTICES.md +75 -0
- package/plugins/cursor/agents/art-director.md +54 -0
- package/plugins/cursor/agents/audio-director.md +51 -0
- package/plugins/cursor/agents/critic.md +113 -0
- package/plugins/cursor/agents/lane.md +37 -0
- package/plugins/cursor/agents/level-designer.md +45 -0
- package/plugins/cursor/agents/mobile-qa.md +70 -0
- package/plugins/cursor/agents/player.md +143 -0
- package/plugins/cursor/agents/qa-lead.md +52 -0
- package/plugins/cursor/commands/first-look.md +59 -0
- package/plugins/cursor/commands/flow.md +50 -0
- package/plugins/cursor/commands/lanes.md +38 -0
- package/plugins/cursor/commands/mobile-qa.md +28 -0
- package/plugins/cursor/commands/new.md +94 -0
- package/plugins/cursor/commands/playtest.md +66 -0
- package/plugins/cursor/commands/publish.md +23 -0
- package/plugins/cursor/commands/smoke-build.md +25 -0
- package/plugins/cursor/commands/verify.md +20 -0
- package/plugins/cursor/hooks/brief.md +135 -0
- package/plugins/cursor/hooks/critic-receipts.mjs +86 -0
- package/plugins/cursor/hooks/durable.mjs +56 -0
- package/plugins/cursor/hooks/fence.mjs +30 -0
- package/plugins/cursor/hooks/floors.mjs +181 -0
- package/plugins/cursor/hooks/flow.mjs +154 -0
- package/plugins/cursor/hooks/front-door.mjs +68 -0
- package/plugins/cursor/hooks/guidance.json +3 -0
- package/plugins/cursor/hooks/hints.mjs +183 -0
- package/plugins/cursor/hooks/hooks.json +26 -0
- package/plugins/cursor/hooks/lib.mjs +362 -0
- package/plugins/cursor/hooks/memory.mjs +92 -0
- package/plugins/cursor/hooks/mobile-qa.mjs +43 -0
- package/plugins/cursor/hooks/note.mjs +63 -0
- package/plugins/cursor/hooks/nudge.mjs +98 -0
- package/plugins/cursor/hooks/pipeline.mjs +207 -0
- package/plugins/cursor/hooks/prompt.mjs +51 -0
- package/plugins/cursor/hooks/quality.mjs +310 -0
- package/plugins/cursor/hooks/reach.mjs +93 -0
- package/plugins/cursor/hooks/scope.mjs +44 -0
- package/plugins/cursor/hooks/session-log.mjs +101 -0
- package/plugins/cursor/hooks/session-start.mjs +194 -0
- package/plugins/cursor/hooks/stop-gate.mjs +303 -0
- package/plugins/cursor/hooks/vendor/direct/round.mjs +81 -0
- package/plugins/cursor/hooks/vendor/find/names.mjs +46 -0
- package/plugins/cursor/hooks/vendor/firstlook/set.mjs +219 -0
- package/plugins/cursor/hooks/vendor/flow/flow.mjs +456 -0
- package/plugins/cursor/hooks/vendor/flow/look.mjs +140 -0
- package/plugins/cursor/hooks/vendor/gdd/book.mjs +484 -0
- package/plugins/cursor/hooks/vendor/gdd/chapters.mjs +72 -0
- package/plugins/cursor/hooks/vendor/gdd/doc.mjs +33 -0
- package/plugins/cursor/hooks/vendor/gdd/levelmap.mjs +429 -0
- package/plugins/cursor/hooks/vendor/gdd/rubric.mjs +251 -0
- package/plugins/cursor/hooks/vendor/gdd/worth.mjs +508 -0
- package/plugins/cursor/hooks/vendor/lanes/fence.mjs +109 -0
- package/plugins/cursor/hooks/vendor/lanes/lanes.mjs +356 -0
- package/plugins/cursor/hooks/vendor/lanes/union.mjs +63 -0
- package/plugins/cursor/hooks/vendor/lessons/motifs.mjs +98 -0
- package/plugins/cursor/hooks/vendor/lessons/store.mjs +57 -0
- package/plugins/cursor/hooks/vendor/look/made-of.mjs +243 -0
- package/plugins/cursor/hooks/vendor/look/roles.mjs +110 -0
- package/plugins/cursor/hooks/vendor/memory/decisions.mjs +139 -0
- package/plugins/cursor/hooks/vendor/memory/journal.mjs +109 -0
- package/plugins/cursor/hooks/vendor/owner/bar.mjs +286 -0
- package/plugins/cursor/hooks/vendor/pipeline/pipeline.mjs +930 -0
- package/plugins/cursor/hooks/vendor/playtest/keep.mjs +111 -0
- package/plugins/cursor/hooks/vendor/playtest/stuck.mjs +48 -0
- package/plugins/cursor/hooks/vendor/progress/holds.mjs +107 -0
- package/plugins/cursor/hooks/vendor/progress/progress.mjs +301 -0
- package/plugins/cursor/hooks/vendor/sim/judge.mjs +241 -0
- package/plugins/cursor/hooks/vendor/spec/rooms.mjs +189 -0
- package/plugins/cursor/hooks/vendor/spec/tunables.mjs +75 -0
- package/plugins/cursor/hooks/vendor/stage/harnessignore.mjs +91 -0
- package/plugins/cursor/hooks/vendor/stage/stage.mjs +58 -0
- package/plugins/cursor/hooks/vendor/status/board.mjs +173 -0
- package/plugins/cursor/hooks/vendor/status/file.mjs +113 -0
- package/plugins/cursor/hooks/vendor/studio/forks.mjs +94 -0
- package/plugins/cursor/hooks/vendor/studio/pitch.mjs +295 -0
- package/plugins/cursor/hooks/vendor/verify/fingerprint.mjs +73 -0
- package/plugins/cursor/hooks/vendor/verify/reads.mjs +33 -0
- package/plugins/cursor/hooks/vendor/wow/picks.mjs +75 -0
- package/plugins/cursor/mcp.json +10 -0
- package/plugins/cursor/rules/funpixel-brief.mdc +141 -0
- package/plugins/cursor/skills/funpixel-3d/SKILL.md +89 -0
- package/plugins/cursor/skills/funpixel-3d/references/bake-off.md +48 -0
- package/plugins/cursor/skills/funpixel-3d/references/bench.md +103 -0
- package/plugins/cursor/skills/funpixel-3d/references/budgets.md +48 -0
- package/plugins/cursor/skills/funpixel-3d/references/characters.md +67 -0
- package/plugins/cursor/skills/funpixel-3d/references/concept-image.md +47 -0
- package/plugins/cursor/skills/funpixel-3d/references/engine-targets.md +31 -0
- package/plugins/cursor/skills/funpixel-3d/references/hero-route.md +104 -0
- package/plugins/cursor/skills/funpixel-3d/references/level-spec.md +109 -0
- package/plugins/cursor/skills/funpixel-3d/references/levels.md +84 -0
- package/plugins/cursor/skills/funpixel-3d/references/light-and-places.md +141 -0
- package/plugins/cursor/skills/funpixel-3d/references/models.md +111 -0
- package/plugins/cursor/skills/funpixel-3d/references/painted-target.md +184 -0
- package/plugins/cursor/skills/funpixel-3d/references/reading.md +41 -0
- package/plugins/cursor/skills/funpixel-3d/references/skin.md +85 -0
- package/plugins/cursor/skills/funpixel-3d/references/skyboxes.md +65 -0
- package/plugins/cursor/skills/funpixel-3d/references/textures.md +71 -0
- package/plugins/cursor/skills/funpixel-3d/references/three-loading.md +199 -0
- package/plugins/cursor/skills/funpixel-3d/references/unity.md +78 -0
- package/plugins/cursor/skills/funpixel-art/SKILL.md +104 -0
- package/plugins/cursor/skills/funpixel-art/references/2d-assets.md +108 -0
- package/plugins/cursor/skills/funpixel-art/references/animation.md +126 -0
- package/plugins/cursor/skills/funpixel-art/references/cast.md +73 -0
- package/plugins/cursor/skills/funpixel-art/references/characters.md +49 -0
- package/plugins/cursor/skills/funpixel-art/references/cost.md +107 -0
- package/plugins/cursor/skills/funpixel-art/references/effects.md +85 -0
- package/plugins/cursor/skills/funpixel-art/references/frames.md +87 -0
- package/plugins/cursor/skills/funpixel-art/references/judging-frames.md +64 -0
- package/plugins/cursor/skills/funpixel-art/references/look.md +102 -0
- package/plugins/cursor/skills/funpixel-art/references/measure.md +119 -0
- package/plugins/cursor/skills/funpixel-art/references/pipeline.md +185 -0
- package/plugins/cursor/skills/funpixel-art/references/pixi.md +277 -0
- package/plugins/cursor/skills/funpixel-art/references/planes.md +27 -0
- package/plugins/cursor/skills/funpixel-art/references/prompts.md +171 -0
- package/plugins/cursor/skills/funpixel-art/references/sheets.md +141 -0
- package/plugins/cursor/skills/funpixel-art/references/style-file.md +39 -0
- package/plugins/cursor/skills/funpixel-art/references/three.md +232 -0
- package/plugins/cursor/skills/funpixel-art/references/ui.md +163 -0
- package/plugins/cursor/skills/funpixel-art/references/unity.md +78 -0
- package/plugins/cursor/skills/funpixel-art/references/wiring.md +41 -0
- package/plugins/cursor/skills/funpixel-audio/SKILL.md +92 -0
- package/plugins/cursor/skills/funpixel-audio/references/adaptive-music.md +204 -0
- package/plugins/cursor/skills/funpixel-audio/references/engine.md +78 -0
- package/plugins/cursor/skills/funpixel-audio/references/measure.md +101 -0
- package/plugins/cursor/skills/funpixel-audio/references/unity.md +74 -0
- package/plugins/cursor/skills/funpixel-feedback/SKILL.md +120 -0
- package/plugins/cursor/skills/funpixel-feedback/references/memory.md +99 -0
- package/plugins/cursor/skills/funpixel-game-feel/SKILL.md +104 -0
- package/plugins/cursor/skills/funpixel-game-feel/references/contact.md +110 -0
- package/plugins/cursor/skills/funpixel-game-feel/references/measure.md +125 -0
- package/plugins/cursor/skills/funpixel-game-feel/references/motion.md +134 -0
- package/plugins/cursor/skills/funpixel-game-feel/references/payoffs.md +35 -0
- package/plugins/cursor/skills/funpixel-game-feel/references/recipes.md +107 -0
- package/plugins/cursor/skills/funpixel-game-feel/references/unity.md +73 -0
- package/plugins/cursor/skills/funpixel-game-logic/SKILL.md +113 -0
- package/plugins/cursor/skills/funpixel-game-logic/references/balance.md +88 -0
- package/plugins/cursor/skills/funpixel-game-logic/references/content-at-scale.md +147 -0
- package/plugins/cursor/skills/funpixel-game-logic/references/depth.md +89 -0
- package/plugins/cursor/skills/funpixel-game-logic/references/escalation.md +41 -0
- package/plugins/cursor/skills/funpixel-game-logic/references/levels.md +61 -0
- package/plugins/cursor/skills/funpixel-game-logic/references/probes.md +122 -0
- package/plugins/cursor/skills/funpixel-game-logic/references/unity.md +108 -0
- package/plugins/cursor/skills/funpixel-game-spine/SKILL.md +118 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/camera.md +94 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/multiplayer.md +101 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/patterns.md +103 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/performance.md +96 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/pixi.md +26 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/replay.md +102 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/room-api.md +63 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/room-modes.md +40 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/sdk.md +114 -0
- package/plugins/cursor/skills/funpixel-game-spine/references/three.md +62 -0
- package/plugins/cursor/skills/funpixel-gdd/SKILL.md +116 -0
- package/plugins/cursor/skills/funpixel-gdd/references/as-built.md +40 -0
- package/plugins/cursor/skills/funpixel-gdd/references/design-measures.md +24 -0
- package/plugins/cursor/skills/funpixel-gdd/references/design-note.md +88 -0
- package/plugins/cursor/skills/funpixel-gdd/references/experience.md +92 -0
- package/plugins/cursor/skills/funpixel-gdd/references/first-look.md +226 -0
- package/plugins/cursor/skills/funpixel-gdd/references/floors.md +62 -0
- package/plugins/cursor/skills/funpixel-gdd/references/frame.md +75 -0
- package/plugins/cursor/skills/funpixel-gdd/references/gameplay-contract.md +81 -0
- package/plugins/cursor/skills/funpixel-gdd/references/gdd-template.md +417 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-card.md +109 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-platformer.md +159 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-puzzle.md +145 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-racing.md +137 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-rpg.md +125 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-shooter.md +128 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-simulation.md +112 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-strategy.md +116 -0
- package/plugins/cursor/skills/funpixel-gdd/references/genre-survival.md +122 -0
- package/plugins/cursor/skills/funpixel-gdd/references/in-the-loop.md +106 -0
- package/plugins/cursor/skills/funpixel-gdd/references/invent-first.md +131 -0
- package/plugins/cursor/skills/funpixel-gdd/references/level-and-fun.md +112 -0
- package/plugins/cursor/skills/funpixel-gdd/references/pages.md +47 -0
- package/plugins/cursor/skills/funpixel-gdd/references/plan-at-size.md +140 -0
- package/plugins/cursor/skills/funpixel-gdd/references/production-tiers.md +117 -0
- package/plugins/cursor/skills/funpixel-gdd/references/sections.md +148 -0
- package/plugins/cursor/skills/funpixel-gdd/references/spec-folder.md +131 -0
- package/plugins/cursor/skills/funpixel-gdd/references/start.md +110 -0
- package/plugins/cursor/skills/funpixel-gdd/references/starters.md +175 -0
- package/plugins/cursor/skills/funpixel-gdd/references/the-path.md +133 -0
- package/plugins/cursor/skills/funpixel-gdd/references/track.md +29 -0
- package/plugins/cursor/skills/funpixel-gdd/references/world-and-identity.md +165 -0
- package/plugins/cursor/skills/funpixel-gdd/references/worth.md +74 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/SKILL.md +107 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/references/contrast.md +123 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/references/hud-and-title.md +108 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/references/hud-genres.md +124 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/references/phone-check.md +156 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/references/recipes.md +279 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/references/restyle-ui.md +229 -0
- package/plugins/cursor/skills/funpixel-mobile-ui/references/unity.md +61 -0
- package/plugins/cursor/skills/funpixel-publish/SKILL.md +109 -0
- package/plugins/cursor/skills/funpixel-publish/references/compose.md +113 -0
- package/plugins/cursor/skills/funpixel-publish/references/motion.md +120 -0
- package/plugins/cursor/skills/funpixel-publish/references/networks.md +75 -0
- package/plugins/cursor/skills/funpixel-publish/references/other-hosts.md +65 -0
- package/plugins/cursor/skills/funpixel-publish/references/playable-ad.md +90 -0
- package/plugins/cursor/skills/funpixel-publish/references/server-build.md +85 -0
- package/plugins/cursor/skills/funpixel-publish/references/store-shots.md +85 -0
- package/plugins/cursor/skills/funpixel-publish/references/trailer.md +55 -0
- package/plugins/cursor/skills/funpixel-publish/references/what-ships.md +30 -0
- package/plugins/cursor/skills/funpixel-store/SKILL.md +81 -0
- package/plugins/cursor/skills/funpixel-store/references/app-store.md +73 -0
- package/plugins/cursor/skills/funpixel-store/references/google-play.md +141 -0
- package/plugins/cursor/skills/funpixel-store/references/ios-release.md +63 -0
- package/plugins/cursor/skills/funpixel-store/references/money.md +66 -0
- package/plugins/cursor/skills/funpixel-store/references/purchases-and-review.md +74 -0
- package/plugins/cursor/skills/funpixel-store/references/readiness-page.md +24 -0
- package/plugins/cursor/skills/funpixel-store/references/readiness.md +107 -0
- package/plugins/cursor/skills/funpixel-store/references/steam.md +38 -0
- package/plugins/cursor/skills/funpixel-store/references/unity-store.md +55 -0
- package/plugins/cursor/skills/funpixel-store/references/web-portals.md +43 -0
- package/plugins/cursor/skills/funpixel-unity-game/SKILL.md +103 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/agent-ops.md +54 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/fixes.md +40 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/from-a-web-game.md +108 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/handing-off.md +42 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/performance.md +59 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/probe.md +80 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/shared-mac.md +121 -0
- package/plugins/cursor/skills/funpixel-unity-game/references/traps.md +108 -0
- package/plugins/cursor/skills/funpixel-verify/SKILL.md +109 -0
- package/plugins/cursor/skills/funpixel-verify/references/actors.md +94 -0
- package/plugins/cursor/skills/funpixel-verify/references/bar.md +62 -0
- package/plugins/cursor/skills/funpixel-verify/references/checks.md +69 -0
- package/plugins/cursor/skills/funpixel-verify/references/experience.md +67 -0
- package/plugins/cursor/skills/funpixel-verify/references/eyes.md +54 -0
- package/plugins/cursor/skills/funpixel-verify/references/fidelity.md +126 -0
- package/plugins/cursor/skills/funpixel-verify/references/hit-bar.md +31 -0
- package/plugins/cursor/skills/funpixel-verify/references/journeys.md +77 -0
- package/plugins/cursor/skills/funpixel-verify/references/mobile-qa.md +123 -0
- package/plugins/cursor/skills/funpixel-verify/references/playtest.md +161 -0
- package/plugins/cursor/skills/funpixel-verify/references/review.md +105 -0
- package/plugins/cursor/skills/funpixel-verify/references/rubric.md +67 -0
- package/plugins/cursor/skills/funpixel-verify/references/scenes.md +49 -0
- package/plugins/cursor/skills/funpixel-verify/references/unity.md +60 -0
- package/plugins/cursor/skills/funpixel-world-and-story/SKILL.md +99 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/atmosphere.md +208 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/forged-world.md +79 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/living-world.md +237 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/opening-and-chapters.md +215 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/shelf.md +80 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/story-at-size.md +159 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/voice.md +175 -0
- package/plugins/cursor/skills/funpixel-world-and-story/references/world-kit.md +154 -0
- package/plugins/kiro/AGENTS.md +135 -0
- package/plugins/kiro/LICENSE +115 -0
- package/plugins/kiro/README.md +17 -0
- package/plugins/kiro/THIRD_PARTY_NOTICES.md +75 -0
- package/plugins/kiro/agent.template.json +45 -0
- package/plugins/kiro/hooks/brief.md +135 -0
- package/plugins/kiro/hooks/critic-receipts.mjs +86 -0
- package/plugins/kiro/hooks/durable.mjs +56 -0
- package/plugins/kiro/hooks/fence.mjs +30 -0
- package/plugins/kiro/hooks/floors.mjs +181 -0
- package/plugins/kiro/hooks/flow.mjs +154 -0
- package/plugins/kiro/hooks/front-door.mjs +68 -0
- package/plugins/kiro/hooks/guidance.json +3 -0
- package/plugins/kiro/hooks/hints.mjs +183 -0
- package/plugins/kiro/hooks/host.md +43 -0
- package/plugins/kiro/hooks/kiro-prompt.mjs +26 -0
- package/plugins/kiro/hooks/lib.mjs +362 -0
- package/plugins/kiro/hooks/memory.mjs +92 -0
- package/plugins/kiro/hooks/mobile-qa.mjs +43 -0
- package/plugins/kiro/hooks/note.mjs +63 -0
- package/plugins/kiro/hooks/nudge.mjs +98 -0
- package/plugins/kiro/hooks/pipeline.mjs +207 -0
- package/plugins/kiro/hooks/prompt.mjs +51 -0
- package/plugins/kiro/hooks/quality.mjs +310 -0
- package/plugins/kiro/hooks/reach.mjs +93 -0
- package/plugins/kiro/hooks/scope.mjs +44 -0
- package/plugins/kiro/hooks/session-log.mjs +101 -0
- package/plugins/kiro/hooks/session-start.mjs +194 -0
- package/plugins/kiro/hooks/stop-gate.mjs +303 -0
- package/plugins/kiro/hooks/vendor/direct/round.mjs +81 -0
- package/plugins/kiro/hooks/vendor/find/names.mjs +46 -0
- package/plugins/kiro/hooks/vendor/firstlook/set.mjs +219 -0
- package/plugins/kiro/hooks/vendor/flow/flow.mjs +456 -0
- package/plugins/kiro/hooks/vendor/flow/look.mjs +140 -0
- package/plugins/kiro/hooks/vendor/gdd/book.mjs +484 -0
- package/plugins/kiro/hooks/vendor/gdd/chapters.mjs +72 -0
- package/plugins/kiro/hooks/vendor/gdd/doc.mjs +33 -0
- package/plugins/kiro/hooks/vendor/gdd/levelmap.mjs +429 -0
- package/plugins/kiro/hooks/vendor/gdd/rubric.mjs +251 -0
- package/plugins/kiro/hooks/vendor/gdd/worth.mjs +508 -0
- package/plugins/kiro/hooks/vendor/lanes/fence.mjs +109 -0
- package/plugins/kiro/hooks/vendor/lanes/lanes.mjs +356 -0
- package/plugins/kiro/hooks/vendor/lanes/union.mjs +63 -0
- package/plugins/kiro/hooks/vendor/lessons/motifs.mjs +98 -0
- package/plugins/kiro/hooks/vendor/lessons/store.mjs +57 -0
- package/plugins/kiro/hooks/vendor/look/made-of.mjs +243 -0
- package/plugins/kiro/hooks/vendor/look/roles.mjs +110 -0
- package/plugins/kiro/hooks/vendor/memory/decisions.mjs +139 -0
- package/plugins/kiro/hooks/vendor/memory/journal.mjs +109 -0
- package/plugins/kiro/hooks/vendor/owner/bar.mjs +286 -0
- package/plugins/kiro/hooks/vendor/pipeline/pipeline.mjs +930 -0
- package/plugins/kiro/hooks/vendor/playtest/keep.mjs +111 -0
- package/plugins/kiro/hooks/vendor/playtest/stuck.mjs +48 -0
- package/plugins/kiro/hooks/vendor/progress/holds.mjs +107 -0
- package/plugins/kiro/hooks/vendor/progress/progress.mjs +301 -0
- package/plugins/kiro/hooks/vendor/sim/judge.mjs +241 -0
- package/plugins/kiro/hooks/vendor/spec/rooms.mjs +189 -0
- package/plugins/kiro/hooks/vendor/spec/tunables.mjs +75 -0
- package/plugins/kiro/hooks/vendor/stage/harnessignore.mjs +91 -0
- package/plugins/kiro/hooks/vendor/stage/stage.mjs +58 -0
- package/plugins/kiro/hooks/vendor/status/board.mjs +173 -0
- package/plugins/kiro/hooks/vendor/status/file.mjs +113 -0
- package/plugins/kiro/hooks/vendor/studio/forks.mjs +94 -0
- package/plugins/kiro/hooks/vendor/studio/pitch.mjs +295 -0
- package/plugins/kiro/hooks/vendor/verify/fingerprint.mjs +73 -0
- package/plugins/kiro/hooks/vendor/verify/reads.mjs +33 -0
- package/plugins/kiro/hooks/vendor/wow/picks.mjs +75 -0
- package/plugins/kiro/install.mjs +37 -0
- package/plugins/kiro/skills/fp-art-director/SKILL.md +58 -0
- package/plugins/kiro/skills/fp-art-director/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-audio-director/SKILL.md +55 -0
- package/plugins/kiro/skills/fp-audio-director/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-critic/SKILL.md +117 -0
- package/plugins/kiro/skills/fp-critic/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-first-look/SKILL.md +61 -0
- package/plugins/kiro/skills/fp-flow/SKILL.md +52 -0
- package/plugins/kiro/skills/fp-lane/SKILL.md +41 -0
- package/plugins/kiro/skills/fp-lane/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-lanes/SKILL.md +40 -0
- package/plugins/kiro/skills/fp-level-designer/SKILL.md +49 -0
- package/plugins/kiro/skills/fp-level-designer/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-mobile-qa/SKILL.md +30 -0
- package/plugins/kiro/skills/fp-mobile-qa-seat/SKILL.md +74 -0
- package/plugins/kiro/skills/fp-mobile-qa-seat/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-new/SKILL.md +96 -0
- package/plugins/kiro/skills/fp-player/SKILL.md +147 -0
- package/plugins/kiro/skills/fp-player/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-playtest/SKILL.md +68 -0
- package/plugins/kiro/skills/fp-publish/SKILL.md +25 -0
- package/plugins/kiro/skills/fp-qa-lead/SKILL.md +56 -0
- package/plugins/kiro/skills/fp-qa-lead/references/kiro.md +43 -0
- package/plugins/kiro/skills/fp-smoke-build/SKILL.md +27 -0
- package/plugins/kiro/skills/fp-studio/SKILL.md +48 -0
- package/plugins/kiro/skills/fp-verify/SKILL.md +22 -0
- package/plugins/kiro/skills/funpixel-3d/SKILL.md +89 -0
- package/plugins/kiro/skills/funpixel-3d/references/bake-off.md +48 -0
- package/plugins/kiro/skills/funpixel-3d/references/bench.md +103 -0
- package/plugins/kiro/skills/funpixel-3d/references/budgets.md +48 -0
- package/plugins/kiro/skills/funpixel-3d/references/characters.md +67 -0
- package/plugins/kiro/skills/funpixel-3d/references/concept-image.md +47 -0
- package/plugins/kiro/skills/funpixel-3d/references/engine-targets.md +31 -0
- package/plugins/kiro/skills/funpixel-3d/references/hero-route.md +104 -0
- package/plugins/kiro/skills/funpixel-3d/references/level-spec.md +109 -0
- package/plugins/kiro/skills/funpixel-3d/references/levels.md +84 -0
- package/plugins/kiro/skills/funpixel-3d/references/light-and-places.md +141 -0
- package/plugins/kiro/skills/funpixel-3d/references/models.md +111 -0
- package/plugins/kiro/skills/funpixel-3d/references/painted-target.md +184 -0
- package/plugins/kiro/skills/funpixel-3d/references/reading.md +41 -0
- package/plugins/kiro/skills/funpixel-3d/references/skin.md +85 -0
- package/plugins/kiro/skills/funpixel-3d/references/skyboxes.md +65 -0
- package/plugins/kiro/skills/funpixel-3d/references/textures.md +71 -0
- package/plugins/kiro/skills/funpixel-3d/references/three-loading.md +199 -0
- package/plugins/kiro/skills/funpixel-3d/references/unity.md +78 -0
- package/plugins/kiro/skills/funpixel-art/SKILL.md +104 -0
- package/plugins/kiro/skills/funpixel-art/references/2d-assets.md +108 -0
- package/plugins/kiro/skills/funpixel-art/references/animation.md +126 -0
- package/plugins/kiro/skills/funpixel-art/references/cast.md +73 -0
- package/plugins/kiro/skills/funpixel-art/references/characters.md +49 -0
- package/plugins/kiro/skills/funpixel-art/references/cost.md +107 -0
- package/plugins/kiro/skills/funpixel-art/references/effects.md +85 -0
- package/plugins/kiro/skills/funpixel-art/references/frames.md +87 -0
- package/plugins/kiro/skills/funpixel-art/references/judging-frames.md +64 -0
- package/plugins/kiro/skills/funpixel-art/references/look.md +102 -0
- package/plugins/kiro/skills/funpixel-art/references/measure.md +119 -0
- package/plugins/kiro/skills/funpixel-art/references/pipeline.md +185 -0
- package/plugins/kiro/skills/funpixel-art/references/pixi.md +277 -0
- package/plugins/kiro/skills/funpixel-art/references/planes.md +27 -0
- package/plugins/kiro/skills/funpixel-art/references/prompts.md +171 -0
- package/plugins/kiro/skills/funpixel-art/references/sheets.md +141 -0
- package/plugins/kiro/skills/funpixel-art/references/style-file.md +39 -0
- package/plugins/kiro/skills/funpixel-art/references/three.md +232 -0
- package/plugins/kiro/skills/funpixel-art/references/ui.md +163 -0
- package/plugins/kiro/skills/funpixel-art/references/unity.md +78 -0
- package/plugins/kiro/skills/funpixel-art/references/wiring.md +41 -0
- package/plugins/kiro/skills/funpixel-audio/SKILL.md +92 -0
- package/plugins/kiro/skills/funpixel-audio/references/adaptive-music.md +204 -0
- package/plugins/kiro/skills/funpixel-audio/references/engine.md +78 -0
- package/plugins/kiro/skills/funpixel-audio/references/measure.md +101 -0
- package/plugins/kiro/skills/funpixel-audio/references/unity.md +74 -0
- package/plugins/kiro/skills/funpixel-feedback/SKILL.md +120 -0
- package/plugins/kiro/skills/funpixel-feedback/references/memory.md +99 -0
- package/plugins/kiro/skills/funpixel-game-feel/SKILL.md +104 -0
- package/plugins/kiro/skills/funpixel-game-feel/references/contact.md +110 -0
- package/plugins/kiro/skills/funpixel-game-feel/references/measure.md +125 -0
- package/plugins/kiro/skills/funpixel-game-feel/references/motion.md +134 -0
- package/plugins/kiro/skills/funpixel-game-feel/references/payoffs.md +35 -0
- package/plugins/kiro/skills/funpixel-game-feel/references/recipes.md +107 -0
- package/plugins/kiro/skills/funpixel-game-feel/references/unity.md +73 -0
- package/plugins/kiro/skills/funpixel-game-logic/SKILL.md +113 -0
- package/plugins/kiro/skills/funpixel-game-logic/references/balance.md +88 -0
- package/plugins/kiro/skills/funpixel-game-logic/references/content-at-scale.md +147 -0
- package/plugins/kiro/skills/funpixel-game-logic/references/depth.md +89 -0
- package/plugins/kiro/skills/funpixel-game-logic/references/escalation.md +41 -0
- package/plugins/kiro/skills/funpixel-game-logic/references/levels.md +61 -0
- package/plugins/kiro/skills/funpixel-game-logic/references/probes.md +122 -0
- package/plugins/kiro/skills/funpixel-game-logic/references/unity.md +108 -0
- package/plugins/kiro/skills/funpixel-game-spine/SKILL.md +118 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/camera.md +94 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/multiplayer.md +101 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/patterns.md +103 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/performance.md +96 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/pixi.md +26 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/replay.md +102 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/room-api.md +63 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/room-modes.md +40 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/sdk.md +114 -0
- package/plugins/kiro/skills/funpixel-game-spine/references/three.md +62 -0
- package/plugins/kiro/skills/funpixel-gdd/SKILL.md +116 -0
- package/plugins/kiro/skills/funpixel-gdd/references/as-built.md +40 -0
- package/plugins/kiro/skills/funpixel-gdd/references/design-measures.md +24 -0
- package/plugins/kiro/skills/funpixel-gdd/references/design-note.md +88 -0
- package/plugins/kiro/skills/funpixel-gdd/references/experience.md +92 -0
- package/plugins/kiro/skills/funpixel-gdd/references/first-look.md +226 -0
- package/plugins/kiro/skills/funpixel-gdd/references/floors.md +62 -0
- package/plugins/kiro/skills/funpixel-gdd/references/frame.md +75 -0
- package/plugins/kiro/skills/funpixel-gdd/references/gameplay-contract.md +81 -0
- package/plugins/kiro/skills/funpixel-gdd/references/gdd-template.md +417 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-card.md +109 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-platformer.md +159 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-puzzle.md +145 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-racing.md +137 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-rpg.md +125 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-shooter.md +128 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-simulation.md +112 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-strategy.md +116 -0
- package/plugins/kiro/skills/funpixel-gdd/references/genre-survival.md +122 -0
- package/plugins/kiro/skills/funpixel-gdd/references/in-the-loop.md +106 -0
- package/plugins/kiro/skills/funpixel-gdd/references/invent-first.md +131 -0
- package/plugins/kiro/skills/funpixel-gdd/references/level-and-fun.md +112 -0
- package/plugins/kiro/skills/funpixel-gdd/references/pages.md +47 -0
- package/plugins/kiro/skills/funpixel-gdd/references/plan-at-size.md +140 -0
- package/plugins/kiro/skills/funpixel-gdd/references/production-tiers.md +117 -0
- package/plugins/kiro/skills/funpixel-gdd/references/sections.md +148 -0
- package/plugins/kiro/skills/funpixel-gdd/references/spec-folder.md +131 -0
- package/plugins/kiro/skills/funpixel-gdd/references/start.md +110 -0
- package/plugins/kiro/skills/funpixel-gdd/references/starters.md +175 -0
- package/plugins/kiro/skills/funpixel-gdd/references/the-path.md +133 -0
- package/plugins/kiro/skills/funpixel-gdd/references/track.md +29 -0
- package/plugins/kiro/skills/funpixel-gdd/references/world-and-identity.md +165 -0
- package/plugins/kiro/skills/funpixel-gdd/references/worth.md +74 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/SKILL.md +107 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/references/contrast.md +123 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/references/hud-and-title.md +108 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/references/hud-genres.md +124 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/references/phone-check.md +156 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/references/recipes.md +279 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/references/restyle-ui.md +229 -0
- package/plugins/kiro/skills/funpixel-mobile-ui/references/unity.md +61 -0
- package/plugins/kiro/skills/funpixel-publish/SKILL.md +109 -0
- package/plugins/kiro/skills/funpixel-publish/references/compose.md +113 -0
- package/plugins/kiro/skills/funpixel-publish/references/motion.md +120 -0
- package/plugins/kiro/skills/funpixel-publish/references/networks.md +75 -0
- package/plugins/kiro/skills/funpixel-publish/references/other-hosts.md +65 -0
- package/plugins/kiro/skills/funpixel-publish/references/playable-ad.md +90 -0
- package/plugins/kiro/skills/funpixel-publish/references/server-build.md +85 -0
- package/plugins/kiro/skills/funpixel-publish/references/store-shots.md +85 -0
- package/plugins/kiro/skills/funpixel-publish/references/trailer.md +55 -0
- package/plugins/kiro/skills/funpixel-publish/references/what-ships.md +30 -0
- package/plugins/kiro/skills/funpixel-store/SKILL.md +81 -0
- package/plugins/kiro/skills/funpixel-store/references/app-store.md +73 -0
- package/plugins/kiro/skills/funpixel-store/references/google-play.md +141 -0
- package/plugins/kiro/skills/funpixel-store/references/ios-release.md +63 -0
- package/plugins/kiro/skills/funpixel-store/references/money.md +66 -0
- package/plugins/kiro/skills/funpixel-store/references/purchases-and-review.md +74 -0
- package/plugins/kiro/skills/funpixel-store/references/readiness-page.md +24 -0
- package/plugins/kiro/skills/funpixel-store/references/readiness.md +107 -0
- package/plugins/kiro/skills/funpixel-store/references/steam.md +38 -0
- package/plugins/kiro/skills/funpixel-store/references/unity-store.md +55 -0
- package/plugins/kiro/skills/funpixel-store/references/web-portals.md +43 -0
- package/plugins/kiro/skills/funpixel-unity-game/SKILL.md +103 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/agent-ops.md +54 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/fixes.md +40 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/from-a-web-game.md +108 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/handing-off.md +42 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/performance.md +59 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/probe.md +80 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/shared-mac.md +121 -0
- package/plugins/kiro/skills/funpixel-unity-game/references/traps.md +108 -0
- package/plugins/kiro/skills/funpixel-verify/SKILL.md +109 -0
- package/plugins/kiro/skills/funpixel-verify/references/actors.md +94 -0
- package/plugins/kiro/skills/funpixel-verify/references/bar.md +62 -0
- package/plugins/kiro/skills/funpixel-verify/references/checks.md +69 -0
- package/plugins/kiro/skills/funpixel-verify/references/experience.md +67 -0
- package/plugins/kiro/skills/funpixel-verify/references/eyes.md +54 -0
- package/plugins/kiro/skills/funpixel-verify/references/fidelity.md +126 -0
- package/plugins/kiro/skills/funpixel-verify/references/hit-bar.md +31 -0
- package/plugins/kiro/skills/funpixel-verify/references/journeys.md +77 -0
- package/plugins/kiro/skills/funpixel-verify/references/mobile-qa.md +123 -0
- package/plugins/kiro/skills/funpixel-verify/references/playtest.md +161 -0
- package/plugins/kiro/skills/funpixel-verify/references/review.md +105 -0
- package/plugins/kiro/skills/funpixel-verify/references/rubric.md +67 -0
- package/plugins/kiro/skills/funpixel-verify/references/scenes.md +49 -0
- package/plugins/kiro/skills/funpixel-verify/references/unity.md +60 -0
- package/plugins/kiro/skills/funpixel-world-and-story/SKILL.md +99 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/atmosphere.md +208 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/forged-world.md +79 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/living-world.md +237 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/opening-and-chapters.md +215 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/shelf.md +80 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/story-at-size.md +159 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/voice.md +175 -0
- package/plugins/kiro/skills/funpixel-world-and-story/references/world-kit.md +154 -0
- package/plugins/robin/LICENSE +115 -0
- package/plugins/robin/README.md +27 -0
- package/plugins/robin/THIRD_PARTY_NOTICES.md +75 -0
- package/plugins/robin/claude/.claude-plugin/plugin.json +19 -0
- package/plugins/robin/claude/.mcp.json +10 -0
- package/plugins/robin/claude/LICENSE +115 -0
- package/plugins/robin/claude/README.md +14 -0
- package/plugins/robin/claude/THIRD_PARTY_NOTICES.md +75 -0
- package/plugins/robin/claude/agents/art-director.md +54 -0
- package/plugins/robin/claude/agents/audio-director.md +51 -0
- package/plugins/robin/claude/agents/critic.md +113 -0
- package/plugins/robin/claude/agents/lane.md +38 -0
- package/plugins/robin/claude/agents/level-designer.md +45 -0
- package/plugins/robin/claude/agents/mobile-qa.md +71 -0
- package/plugins/robin/claude/agents/player.md +144 -0
- package/plugins/robin/claude/agents/qa-lead.md +52 -0
- package/plugins/robin/claude/commands/first-look.md +59 -0
- package/plugins/robin/claude/commands/flow.md +50 -0
- package/plugins/robin/claude/commands/lanes.md +38 -0
- package/plugins/robin/claude/commands/mobile-qa.md +28 -0
- package/plugins/robin/claude/commands/new.md +94 -0
- package/plugins/robin/claude/commands/playtest.md +66 -0
- package/plugins/robin/claude/commands/publish.md +23 -0
- package/plugins/robin/claude/commands/smoke-build.md +25 -0
- package/plugins/robin/claude/commands/verify.md +20 -0
- package/plugins/robin/claude/hooks/brief.md +135 -0
- package/plugins/robin/claude/hooks/critic-receipts.mjs +86 -0
- package/plugins/robin/claude/hooks/durable.mjs +56 -0
- package/plugins/robin/claude/hooks/fence.mjs +30 -0
- package/plugins/robin/claude/hooks/floors.mjs +181 -0
- package/plugins/robin/claude/hooks/flow.mjs +154 -0
- package/plugins/robin/claude/hooks/front-door.mjs +68 -0
- package/plugins/robin/claude/hooks/guidance.json +3 -0
- package/plugins/robin/claude/hooks/hints.mjs +183 -0
- package/plugins/robin/claude/hooks/hooks.json +62 -0
- package/plugins/robin/claude/hooks/lib.mjs +362 -0
- package/plugins/robin/claude/hooks/memory.mjs +92 -0
- package/plugins/robin/claude/hooks/mobile-qa.mjs +43 -0
- package/plugins/robin/claude/hooks/note.mjs +63 -0
- package/plugins/robin/claude/hooks/nudge.mjs +98 -0
- package/plugins/robin/claude/hooks/pipeline.mjs +207 -0
- package/plugins/robin/claude/hooks/prompt.mjs +51 -0
- package/plugins/robin/claude/hooks/quality.mjs +310 -0
- package/plugins/robin/claude/hooks/reach.mjs +93 -0
- package/plugins/robin/claude/hooks/scope.mjs +44 -0
- package/plugins/robin/claude/hooks/session-log.mjs +101 -0
- package/plugins/robin/claude/hooks/session-start.mjs +194 -0
- package/plugins/robin/claude/hooks/skill-names.json +19 -0
- package/plugins/robin/claude/hooks/stop-gate.mjs +303 -0
- package/plugins/robin/claude/hooks/vendor/direct/round.mjs +81 -0
- package/plugins/robin/claude/hooks/vendor/find/names.mjs +46 -0
- package/plugins/robin/claude/hooks/vendor/firstlook/set.mjs +219 -0
- package/plugins/robin/claude/hooks/vendor/flow/flow.mjs +456 -0
- package/plugins/robin/claude/hooks/vendor/flow/look.mjs +140 -0
- package/plugins/robin/claude/hooks/vendor/gdd/book.mjs +484 -0
- package/plugins/robin/claude/hooks/vendor/gdd/chapters.mjs +72 -0
- package/plugins/robin/claude/hooks/vendor/gdd/doc.mjs +33 -0
- package/plugins/robin/claude/hooks/vendor/gdd/levelmap.mjs +429 -0
- package/plugins/robin/claude/hooks/vendor/gdd/rubric.mjs +251 -0
- package/plugins/robin/claude/hooks/vendor/gdd/worth.mjs +508 -0
- package/plugins/robin/claude/hooks/vendor/lanes/fence.mjs +109 -0
- package/plugins/robin/claude/hooks/vendor/lanes/lanes.mjs +356 -0
- package/plugins/robin/claude/hooks/vendor/lanes/union.mjs +63 -0
- package/plugins/robin/claude/hooks/vendor/lessons/motifs.mjs +98 -0
- package/plugins/robin/claude/hooks/vendor/lessons/store.mjs +57 -0
- package/plugins/robin/claude/hooks/vendor/look/made-of.mjs +243 -0
- package/plugins/robin/claude/hooks/vendor/look/roles.mjs +110 -0
- package/plugins/robin/claude/hooks/vendor/memory/decisions.mjs +139 -0
- package/plugins/robin/claude/hooks/vendor/memory/journal.mjs +109 -0
- package/plugins/robin/claude/hooks/vendor/owner/bar.mjs +286 -0
- package/plugins/robin/claude/hooks/vendor/pipeline/pipeline.mjs +930 -0
- package/plugins/robin/claude/hooks/vendor/playtest/keep.mjs +111 -0
- package/plugins/robin/claude/hooks/vendor/playtest/stuck.mjs +48 -0
- package/plugins/robin/claude/hooks/vendor/progress/holds.mjs +107 -0
- package/plugins/robin/claude/hooks/vendor/progress/progress.mjs +301 -0
- package/plugins/robin/claude/hooks/vendor/sim/judge.mjs +241 -0
- package/plugins/robin/claude/hooks/vendor/spec/rooms.mjs +189 -0
- package/plugins/robin/claude/hooks/vendor/spec/tunables.mjs +75 -0
- package/plugins/robin/claude/hooks/vendor/stage/harnessignore.mjs +91 -0
- package/plugins/robin/claude/hooks/vendor/stage/stage.mjs +58 -0
- package/plugins/robin/claude/hooks/vendor/status/board.mjs +173 -0
- package/plugins/robin/claude/hooks/vendor/status/file.mjs +113 -0
- package/plugins/robin/claude/hooks/vendor/studio/forks.mjs +94 -0
- package/plugins/robin/claude/hooks/vendor/studio/pitch.mjs +295 -0
- package/plugins/robin/claude/hooks/vendor/verify/fingerprint.mjs +73 -0
- package/plugins/robin/claude/hooks/vendor/verify/reads.mjs +33 -0
- package/plugins/robin/claude/hooks/vendor/wow/picks.mjs +75 -0
- package/plugins/robin/claude/skills/3d/SKILL.md +89 -0
- package/plugins/robin/claude/skills/3d/references/bake-off.md +48 -0
- package/plugins/robin/claude/skills/3d/references/bench.md +103 -0
- package/plugins/robin/claude/skills/3d/references/budgets.md +48 -0
- package/plugins/robin/claude/skills/3d/references/characters.md +67 -0
- package/plugins/robin/claude/skills/3d/references/concept-image.md +47 -0
- package/plugins/robin/claude/skills/3d/references/engine-targets.md +31 -0
- package/plugins/robin/claude/skills/3d/references/hero-route.md +104 -0
- package/plugins/robin/claude/skills/3d/references/level-spec.md +109 -0
- package/plugins/robin/claude/skills/3d/references/levels.md +84 -0
- package/plugins/robin/claude/skills/3d/references/light-and-places.md +141 -0
- package/plugins/robin/claude/skills/3d/references/models.md +111 -0
- package/plugins/robin/claude/skills/3d/references/painted-target.md +184 -0
- package/plugins/robin/claude/skills/3d/references/reading.md +41 -0
- package/plugins/robin/claude/skills/3d/references/skin.md +85 -0
- package/plugins/robin/claude/skills/3d/references/skyboxes.md +65 -0
- package/plugins/robin/claude/skills/3d/references/textures.md +71 -0
- package/plugins/robin/claude/skills/3d/references/three-loading.md +199 -0
- package/plugins/robin/claude/skills/3d/references/unity.md +78 -0
- package/plugins/robin/claude/skills/art/SKILL.md +104 -0
- package/plugins/robin/claude/skills/art/references/2d-assets.md +108 -0
- package/plugins/robin/claude/skills/art/references/animation.md +126 -0
- package/plugins/robin/claude/skills/art/references/cast.md +73 -0
- package/plugins/robin/claude/skills/art/references/characters.md +49 -0
- package/plugins/robin/claude/skills/art/references/cost.md +107 -0
- package/plugins/robin/claude/skills/art/references/effects.md +85 -0
- package/plugins/robin/claude/skills/art/references/frames.md +87 -0
- package/plugins/robin/claude/skills/art/references/judging-frames.md +64 -0
- package/plugins/robin/claude/skills/art/references/look.md +102 -0
- package/plugins/robin/claude/skills/art/references/measure.md +119 -0
- package/plugins/robin/claude/skills/art/references/pipeline.md +185 -0
- package/plugins/robin/claude/skills/art/references/pixi.md +277 -0
- package/plugins/robin/claude/skills/art/references/planes.md +27 -0
- package/plugins/robin/claude/skills/art/references/prompts.md +171 -0
- package/plugins/robin/claude/skills/art/references/sheets.md +141 -0
- package/plugins/robin/claude/skills/art/references/style-file.md +39 -0
- package/plugins/robin/claude/skills/art/references/three.md +232 -0
- package/plugins/robin/claude/skills/art/references/ui.md +163 -0
- package/plugins/robin/claude/skills/art/references/unity.md +78 -0
- package/plugins/robin/claude/skills/art/references/wiring.md +41 -0
- package/plugins/robin/claude/skills/audio/SKILL.md +92 -0
- package/plugins/robin/claude/skills/audio/references/adaptive-music.md +204 -0
- package/plugins/robin/claude/skills/audio/references/engine.md +78 -0
- package/plugins/robin/claude/skills/audio/references/measure.md +101 -0
- package/plugins/robin/claude/skills/audio/references/unity.md +74 -0
- package/plugins/robin/claude/skills/feedback/SKILL.md +120 -0
- package/plugins/robin/claude/skills/feedback/references/memory.md +99 -0
- package/plugins/robin/claude/skills/game-feel/SKILL.md +104 -0
- package/plugins/robin/claude/skills/game-feel/references/contact.md +110 -0
- package/plugins/robin/claude/skills/game-feel/references/measure.md +125 -0
- package/plugins/robin/claude/skills/game-feel/references/motion.md +134 -0
- package/plugins/robin/claude/skills/game-feel/references/payoffs.md +35 -0
- package/plugins/robin/claude/skills/game-feel/references/recipes.md +107 -0
- package/plugins/robin/claude/skills/game-feel/references/unity.md +73 -0
- package/plugins/robin/claude/skills/game-logic/SKILL.md +113 -0
- package/plugins/robin/claude/skills/game-logic/references/balance.md +88 -0
- package/plugins/robin/claude/skills/game-logic/references/content-at-scale.md +147 -0
- package/plugins/robin/claude/skills/game-logic/references/depth.md +89 -0
- package/plugins/robin/claude/skills/game-logic/references/escalation.md +41 -0
- package/plugins/robin/claude/skills/game-logic/references/levels.md +61 -0
- package/plugins/robin/claude/skills/game-logic/references/probes.md +122 -0
- package/plugins/robin/claude/skills/game-logic/references/unity.md +108 -0
- package/plugins/robin/claude/skills/game-spine/SKILL.md +118 -0
- package/plugins/robin/claude/skills/game-spine/references/camera.md +94 -0
- package/plugins/robin/claude/skills/game-spine/references/multiplayer.md +101 -0
- package/plugins/robin/claude/skills/game-spine/references/patterns.md +103 -0
- package/plugins/robin/claude/skills/game-spine/references/performance.md +96 -0
- package/plugins/robin/claude/skills/game-spine/references/pixi.md +26 -0
- package/plugins/robin/claude/skills/game-spine/references/replay.md +102 -0
- package/plugins/robin/claude/skills/game-spine/references/room-api.md +63 -0
- package/plugins/robin/claude/skills/game-spine/references/room-modes.md +40 -0
- package/plugins/robin/claude/skills/game-spine/references/sdk.md +114 -0
- package/plugins/robin/claude/skills/game-spine/references/three.md +62 -0
- package/plugins/robin/claude/skills/gdd/SKILL.md +116 -0
- package/plugins/robin/claude/skills/gdd/references/as-built.md +40 -0
- package/plugins/robin/claude/skills/gdd/references/design-measures.md +24 -0
- package/plugins/robin/claude/skills/gdd/references/design-note.md +88 -0
- package/plugins/robin/claude/skills/gdd/references/experience.md +92 -0
- package/plugins/robin/claude/skills/gdd/references/first-look.md +226 -0
- package/plugins/robin/claude/skills/gdd/references/floors.md +62 -0
- package/plugins/robin/claude/skills/gdd/references/frame.md +75 -0
- package/plugins/robin/claude/skills/gdd/references/gameplay-contract.md +81 -0
- package/plugins/robin/claude/skills/gdd/references/gdd-template.md +417 -0
- package/plugins/robin/claude/skills/gdd/references/genre-card.md +109 -0
- package/plugins/robin/claude/skills/gdd/references/genre-platformer.md +159 -0
- package/plugins/robin/claude/skills/gdd/references/genre-puzzle.md +145 -0
- package/plugins/robin/claude/skills/gdd/references/genre-racing.md +137 -0
- package/plugins/robin/claude/skills/gdd/references/genre-rpg.md +125 -0
- package/plugins/robin/claude/skills/gdd/references/genre-shooter.md +128 -0
- package/plugins/robin/claude/skills/gdd/references/genre-simulation.md +112 -0
- package/plugins/robin/claude/skills/gdd/references/genre-strategy.md +116 -0
- package/plugins/robin/claude/skills/gdd/references/genre-survival.md +122 -0
- package/plugins/robin/claude/skills/gdd/references/in-the-loop.md +106 -0
- package/plugins/robin/claude/skills/gdd/references/invent-first.md +131 -0
- package/plugins/robin/claude/skills/gdd/references/level-and-fun.md +112 -0
- package/plugins/robin/claude/skills/gdd/references/pages.md +47 -0
- package/plugins/robin/claude/skills/gdd/references/plan-at-size.md +140 -0
- package/plugins/robin/claude/skills/gdd/references/production-tiers.md +117 -0
- package/plugins/robin/claude/skills/gdd/references/sections.md +148 -0
- package/plugins/robin/claude/skills/gdd/references/spec-folder.md +131 -0
- package/plugins/robin/claude/skills/gdd/references/start.md +110 -0
- package/plugins/robin/claude/skills/gdd/references/starters.md +175 -0
- package/plugins/robin/claude/skills/gdd/references/the-path.md +133 -0
- package/plugins/robin/claude/skills/gdd/references/track.md +29 -0
- package/plugins/robin/claude/skills/gdd/references/world-and-identity.md +165 -0
- package/plugins/robin/claude/skills/gdd/references/worth.md +74 -0
- package/plugins/robin/claude/skills/mobile-ui/SKILL.md +107 -0
- package/plugins/robin/claude/skills/mobile-ui/references/contrast.md +123 -0
- package/plugins/robin/claude/skills/mobile-ui/references/hud-and-title.md +108 -0
- package/plugins/robin/claude/skills/mobile-ui/references/hud-genres.md +124 -0
- package/plugins/robin/claude/skills/mobile-ui/references/phone-check.md +156 -0
- package/plugins/robin/claude/skills/mobile-ui/references/recipes.md +279 -0
- package/plugins/robin/claude/skills/mobile-ui/references/restyle-ui.md +229 -0
- package/plugins/robin/claude/skills/mobile-ui/references/unity.md +61 -0
- package/plugins/robin/claude/skills/publishing/SKILL.md +109 -0
- package/plugins/robin/claude/skills/publishing/references/compose.md +113 -0
- package/plugins/robin/claude/skills/publishing/references/motion.md +120 -0
- package/plugins/robin/claude/skills/publishing/references/networks.md +75 -0
- package/plugins/robin/claude/skills/publishing/references/other-hosts.md +65 -0
- package/plugins/robin/claude/skills/publishing/references/playable-ad.md +90 -0
- package/plugins/robin/claude/skills/publishing/references/server-build.md +85 -0
- package/plugins/robin/claude/skills/publishing/references/store-shots.md +85 -0
- package/plugins/robin/claude/skills/publishing/references/trailer.md +55 -0
- package/plugins/robin/claude/skills/publishing/references/what-ships.md +30 -0
- package/plugins/robin/claude/skills/store/SKILL.md +81 -0
- package/plugins/robin/claude/skills/store/references/app-store.md +73 -0
- package/plugins/robin/claude/skills/store/references/google-play.md +141 -0
- package/plugins/robin/claude/skills/store/references/ios-release.md +63 -0
- package/plugins/robin/claude/skills/store/references/money.md +66 -0
- package/plugins/robin/claude/skills/store/references/purchases-and-review.md +74 -0
- package/plugins/robin/claude/skills/store/references/readiness-page.md +24 -0
- package/plugins/robin/claude/skills/store/references/readiness.md +107 -0
- package/plugins/robin/claude/skills/store/references/steam.md +38 -0
- package/plugins/robin/claude/skills/store/references/unity-store.md +55 -0
- package/plugins/robin/claude/skills/store/references/web-portals.md +43 -0
- package/plugins/robin/claude/skills/unity-game/SKILL.md +103 -0
- package/plugins/robin/claude/skills/unity-game/references/agent-ops.md +54 -0
- package/plugins/robin/claude/skills/unity-game/references/fixes.md +40 -0
- package/plugins/robin/claude/skills/unity-game/references/from-a-web-game.md +108 -0
- package/plugins/robin/claude/skills/unity-game/references/handing-off.md +42 -0
- package/plugins/robin/claude/skills/unity-game/references/performance.md +59 -0
- package/plugins/robin/claude/skills/unity-game/references/probe.md +80 -0
- package/plugins/robin/claude/skills/unity-game/references/shared-mac.md +121 -0
- package/plugins/robin/claude/skills/unity-game/references/traps.md +108 -0
- package/plugins/robin/claude/skills/verifying/SKILL.md +109 -0
- package/plugins/robin/claude/skills/verifying/references/actors.md +94 -0
- package/plugins/robin/claude/skills/verifying/references/bar.md +62 -0
- package/plugins/robin/claude/skills/verifying/references/checks.md +69 -0
- package/plugins/robin/claude/skills/verifying/references/experience.md +67 -0
- package/plugins/robin/claude/skills/verifying/references/eyes.md +54 -0
- package/plugins/robin/claude/skills/verifying/references/fidelity.md +126 -0
- package/plugins/robin/claude/skills/verifying/references/hit-bar.md +31 -0
- package/plugins/robin/claude/skills/verifying/references/journeys.md +77 -0
- package/plugins/robin/claude/skills/verifying/references/mobile-qa.md +123 -0
- package/plugins/robin/claude/skills/verifying/references/playtest.md +161 -0
- package/plugins/robin/claude/skills/verifying/references/review.md +105 -0
- package/plugins/robin/claude/skills/verifying/references/rubric.md +67 -0
- package/plugins/robin/claude/skills/verifying/references/scenes.md +49 -0
- package/plugins/robin/claude/skills/verifying/references/unity.md +60 -0
- package/plugins/robin/claude/skills/world-and-story/SKILL.md +99 -0
- package/plugins/robin/claude/skills/world-and-story/references/atmosphere.md +208 -0
- package/plugins/robin/claude/skills/world-and-story/references/forged-world.md +79 -0
- package/plugins/robin/claude/skills/world-and-story/references/living-world.md +237 -0
- package/plugins/robin/claude/skills/world-and-story/references/opening-and-chapters.md +215 -0
- package/plugins/robin/claude/skills/world-and-story/references/shelf.md +80 -0
- package/plugins/robin/claude/skills/world-and-story/references/story-at-size.md +159 -0
- package/plugins/robin/claude/skills/world-and-story/references/voice.md +175 -0
- package/plugins/robin/claude/skills/world-and-story/references/world-kit.md +154 -0
- package/plugins/robin/mcp-servers.json +25 -0
- package/plugins/robin/plugin.json +20 -0
- package/plugins/robin/skills/fp-first-look/SKILL.md +59 -0
- package/plugins/robin/skills/fp-flow/SKILL.md +50 -0
- package/plugins/robin/skills/fp-lanes/SKILL.md +38 -0
- package/plugins/robin/skills/fp-mobile-qa/SKILL.md +28 -0
- package/plugins/robin/skills/fp-new/SKILL.md +94 -0
- package/plugins/robin/skills/fp-playtest/SKILL.md +66 -0
- package/plugins/robin/skills/fp-publish/SKILL.md +23 -0
- package/plugins/robin/skills/fp-smoke-build/SKILL.md +25 -0
- package/plugins/robin/skills/fp-verify/SKILL.md +20 -0
- package/plugins/robin/skills/funpixel-3d/SKILL.md +89 -0
- package/plugins/robin/skills/funpixel-3d/references/bake-off.md +48 -0
- package/plugins/robin/skills/funpixel-3d/references/bench.md +103 -0
- package/plugins/robin/skills/funpixel-3d/references/budgets.md +48 -0
- package/plugins/robin/skills/funpixel-3d/references/characters.md +67 -0
- package/plugins/robin/skills/funpixel-3d/references/concept-image.md +47 -0
- package/plugins/robin/skills/funpixel-3d/references/engine-targets.md +31 -0
- package/plugins/robin/skills/funpixel-3d/references/hero-route.md +104 -0
- package/plugins/robin/skills/funpixel-3d/references/level-spec.md +109 -0
- package/plugins/robin/skills/funpixel-3d/references/levels.md +84 -0
- package/plugins/robin/skills/funpixel-3d/references/light-and-places.md +141 -0
- package/plugins/robin/skills/funpixel-3d/references/models.md +111 -0
- package/plugins/robin/skills/funpixel-3d/references/painted-target.md +184 -0
- package/plugins/robin/skills/funpixel-3d/references/reading.md +41 -0
- package/plugins/robin/skills/funpixel-3d/references/skin.md +85 -0
- package/plugins/robin/skills/funpixel-3d/references/skyboxes.md +65 -0
- package/plugins/robin/skills/funpixel-3d/references/textures.md +71 -0
- package/plugins/robin/skills/funpixel-3d/references/three-loading.md +199 -0
- package/plugins/robin/skills/funpixel-3d/references/unity.md +78 -0
- package/plugins/robin/skills/funpixel-art/SKILL.md +104 -0
- package/plugins/robin/skills/funpixel-art/references/2d-assets.md +108 -0
- package/plugins/robin/skills/funpixel-art/references/animation.md +126 -0
- package/plugins/robin/skills/funpixel-art/references/cast.md +73 -0
- package/plugins/robin/skills/funpixel-art/references/characters.md +49 -0
- package/plugins/robin/skills/funpixel-art/references/cost.md +107 -0
- package/plugins/robin/skills/funpixel-art/references/effects.md +85 -0
- package/plugins/robin/skills/funpixel-art/references/frames.md +87 -0
- package/plugins/robin/skills/funpixel-art/references/judging-frames.md +64 -0
- package/plugins/robin/skills/funpixel-art/references/look.md +102 -0
- package/plugins/robin/skills/funpixel-art/references/measure.md +119 -0
- package/plugins/robin/skills/funpixel-art/references/pipeline.md +185 -0
- package/plugins/robin/skills/funpixel-art/references/pixi.md +277 -0
- package/plugins/robin/skills/funpixel-art/references/planes.md +27 -0
- package/plugins/robin/skills/funpixel-art/references/prompts.md +171 -0
- package/plugins/robin/skills/funpixel-art/references/sheets.md +141 -0
- package/plugins/robin/skills/funpixel-art/references/style-file.md +39 -0
- package/plugins/robin/skills/funpixel-art/references/three.md +232 -0
- package/plugins/robin/skills/funpixel-art/references/ui.md +163 -0
- package/plugins/robin/skills/funpixel-art/references/unity.md +78 -0
- package/plugins/robin/skills/funpixel-art/references/wiring.md +41 -0
- package/plugins/robin/skills/funpixel-audio/SKILL.md +92 -0
- package/plugins/robin/skills/funpixel-audio/references/adaptive-music.md +204 -0
- package/plugins/robin/skills/funpixel-audio/references/engine.md +78 -0
- package/plugins/robin/skills/funpixel-audio/references/measure.md +101 -0
- package/plugins/robin/skills/funpixel-audio/references/unity.md +74 -0
- package/plugins/robin/skills/funpixel-brief/SKILL.md +163 -0
- package/plugins/robin/skills/funpixel-feedback/SKILL.md +120 -0
- package/plugins/robin/skills/funpixel-feedback/references/memory.md +99 -0
- package/plugins/robin/skills/funpixel-game-feel/SKILL.md +104 -0
- package/plugins/robin/skills/funpixel-game-feel/references/contact.md +110 -0
- package/plugins/robin/skills/funpixel-game-feel/references/measure.md +125 -0
- package/plugins/robin/skills/funpixel-game-feel/references/motion.md +134 -0
- package/plugins/robin/skills/funpixel-game-feel/references/payoffs.md +35 -0
- package/plugins/robin/skills/funpixel-game-feel/references/recipes.md +107 -0
- package/plugins/robin/skills/funpixel-game-feel/references/unity.md +73 -0
- package/plugins/robin/skills/funpixel-game-logic/SKILL.md +113 -0
- package/plugins/robin/skills/funpixel-game-logic/references/balance.md +88 -0
- package/plugins/robin/skills/funpixel-game-logic/references/content-at-scale.md +147 -0
- package/plugins/robin/skills/funpixel-game-logic/references/depth.md +89 -0
- package/plugins/robin/skills/funpixel-game-logic/references/escalation.md +41 -0
- package/plugins/robin/skills/funpixel-game-logic/references/levels.md +61 -0
- package/plugins/robin/skills/funpixel-game-logic/references/probes.md +122 -0
- package/plugins/robin/skills/funpixel-game-logic/references/unity.md +108 -0
- package/plugins/robin/skills/funpixel-game-spine/SKILL.md +118 -0
- package/plugins/robin/skills/funpixel-game-spine/references/camera.md +94 -0
- package/plugins/robin/skills/funpixel-game-spine/references/multiplayer.md +101 -0
- package/plugins/robin/skills/funpixel-game-spine/references/patterns.md +103 -0
- package/plugins/robin/skills/funpixel-game-spine/references/performance.md +96 -0
- package/plugins/robin/skills/funpixel-game-spine/references/pixi.md +26 -0
- package/plugins/robin/skills/funpixel-game-spine/references/replay.md +102 -0
- package/plugins/robin/skills/funpixel-game-spine/references/room-api.md +63 -0
- package/plugins/robin/skills/funpixel-game-spine/references/room-modes.md +40 -0
- package/plugins/robin/skills/funpixel-game-spine/references/sdk.md +114 -0
- package/plugins/robin/skills/funpixel-game-spine/references/three.md +62 -0
- package/plugins/robin/skills/funpixel-gdd/SKILL.md +116 -0
- package/plugins/robin/skills/funpixel-gdd/references/as-built.md +40 -0
- package/plugins/robin/skills/funpixel-gdd/references/design-measures.md +24 -0
- package/plugins/robin/skills/funpixel-gdd/references/design-note.md +88 -0
- package/plugins/robin/skills/funpixel-gdd/references/experience.md +92 -0
- package/plugins/robin/skills/funpixel-gdd/references/first-look.md +226 -0
- package/plugins/robin/skills/funpixel-gdd/references/floors.md +62 -0
- package/plugins/robin/skills/funpixel-gdd/references/frame.md +75 -0
- package/plugins/robin/skills/funpixel-gdd/references/gameplay-contract.md +81 -0
- package/plugins/robin/skills/funpixel-gdd/references/gdd-template.md +417 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-card.md +109 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-platformer.md +159 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-puzzle.md +145 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-racing.md +137 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-rpg.md +125 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-shooter.md +128 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-simulation.md +112 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-strategy.md +116 -0
- package/plugins/robin/skills/funpixel-gdd/references/genre-survival.md +122 -0
- package/plugins/robin/skills/funpixel-gdd/references/in-the-loop.md +106 -0
- package/plugins/robin/skills/funpixel-gdd/references/invent-first.md +131 -0
- package/plugins/robin/skills/funpixel-gdd/references/level-and-fun.md +112 -0
- package/plugins/robin/skills/funpixel-gdd/references/pages.md +47 -0
- package/plugins/robin/skills/funpixel-gdd/references/plan-at-size.md +140 -0
- package/plugins/robin/skills/funpixel-gdd/references/production-tiers.md +117 -0
- package/plugins/robin/skills/funpixel-gdd/references/sections.md +148 -0
- package/plugins/robin/skills/funpixel-gdd/references/spec-folder.md +131 -0
- package/plugins/robin/skills/funpixel-gdd/references/start.md +110 -0
- package/plugins/robin/skills/funpixel-gdd/references/starters.md +175 -0
- package/plugins/robin/skills/funpixel-gdd/references/the-path.md +133 -0
- package/plugins/robin/skills/funpixel-gdd/references/track.md +29 -0
- package/plugins/robin/skills/funpixel-gdd/references/world-and-identity.md +165 -0
- package/plugins/robin/skills/funpixel-gdd/references/worth.md +74 -0
- package/plugins/robin/skills/funpixel-mobile-ui/SKILL.md +107 -0
- package/plugins/robin/skills/funpixel-mobile-ui/references/contrast.md +123 -0
- package/plugins/robin/skills/funpixel-mobile-ui/references/hud-and-title.md +108 -0
- package/plugins/robin/skills/funpixel-mobile-ui/references/hud-genres.md +124 -0
- package/plugins/robin/skills/funpixel-mobile-ui/references/phone-check.md +156 -0
- package/plugins/robin/skills/funpixel-mobile-ui/references/recipes.md +279 -0
- package/plugins/robin/skills/funpixel-mobile-ui/references/restyle-ui.md +229 -0
- package/plugins/robin/skills/funpixel-mobile-ui/references/unity.md +61 -0
- package/plugins/robin/skills/funpixel-publish/SKILL.md +109 -0
- package/plugins/robin/skills/funpixel-publish/references/compose.md +113 -0
- package/plugins/robin/skills/funpixel-publish/references/motion.md +120 -0
- package/plugins/robin/skills/funpixel-publish/references/networks.md +75 -0
- package/plugins/robin/skills/funpixel-publish/references/other-hosts.md +65 -0
- package/plugins/robin/skills/funpixel-publish/references/playable-ad.md +90 -0
- package/plugins/robin/skills/funpixel-publish/references/server-build.md +85 -0
- package/plugins/robin/skills/funpixel-publish/references/store-shots.md +85 -0
- package/plugins/robin/skills/funpixel-publish/references/trailer.md +55 -0
- package/plugins/robin/skills/funpixel-publish/references/what-ships.md +30 -0
- package/plugins/robin/skills/funpixel-store/SKILL.md +81 -0
- package/plugins/robin/skills/funpixel-store/references/app-store.md +73 -0
- package/plugins/robin/skills/funpixel-store/references/google-play.md +141 -0
- package/plugins/robin/skills/funpixel-store/references/ios-release.md +63 -0
- package/plugins/robin/skills/funpixel-store/references/money.md +66 -0
- package/plugins/robin/skills/funpixel-store/references/purchases-and-review.md +74 -0
- package/plugins/robin/skills/funpixel-store/references/readiness-page.md +24 -0
- package/plugins/robin/skills/funpixel-store/references/readiness.md +107 -0
- package/plugins/robin/skills/funpixel-store/references/steam.md +38 -0
- package/plugins/robin/skills/funpixel-store/references/unity-store.md +55 -0
- package/plugins/robin/skills/funpixel-store/references/web-portals.md +43 -0
- package/plugins/robin/skills/funpixel-unity-game/SKILL.md +103 -0
- package/plugins/robin/skills/funpixel-unity-game/references/agent-ops.md +54 -0
- package/plugins/robin/skills/funpixel-unity-game/references/fixes.md +40 -0
- package/plugins/robin/skills/funpixel-unity-game/references/from-a-web-game.md +108 -0
- package/plugins/robin/skills/funpixel-unity-game/references/handing-off.md +42 -0
- package/plugins/robin/skills/funpixel-unity-game/references/performance.md +59 -0
- package/plugins/robin/skills/funpixel-unity-game/references/probe.md +80 -0
- package/plugins/robin/skills/funpixel-unity-game/references/shared-mac.md +121 -0
- package/plugins/robin/skills/funpixel-unity-game/references/traps.md +108 -0
- package/plugins/robin/skills/funpixel-verify/SKILL.md +109 -0
- package/plugins/robin/skills/funpixel-verify/references/actors.md +94 -0
- package/plugins/robin/skills/funpixel-verify/references/bar.md +62 -0
- package/plugins/robin/skills/funpixel-verify/references/checks.md +69 -0
- package/plugins/robin/skills/funpixel-verify/references/experience.md +67 -0
- package/plugins/robin/skills/funpixel-verify/references/eyes.md +54 -0
- package/plugins/robin/skills/funpixel-verify/references/fidelity.md +126 -0
- package/plugins/robin/skills/funpixel-verify/references/hit-bar.md +31 -0
- package/plugins/robin/skills/funpixel-verify/references/journeys.md +77 -0
- package/plugins/robin/skills/funpixel-verify/references/mobile-qa.md +123 -0
- package/plugins/robin/skills/funpixel-verify/references/playtest.md +161 -0
- package/plugins/robin/skills/funpixel-verify/references/review.md +105 -0
- package/plugins/robin/skills/funpixel-verify/references/rubric.md +67 -0
- package/plugins/robin/skills/funpixel-verify/references/scenes.md +49 -0
- package/plugins/robin/skills/funpixel-verify/references/unity.md +60 -0
- package/plugins/robin/skills/funpixel-world-and-story/SKILL.md +99 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/atmosphere.md +208 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/forged-world.md +79 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/living-world.md +237 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/opening-and-chapters.md +215 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/shelf.md +80 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/story-at-size.md +159 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/voice.md +175 -0
- package/plugins/robin/skills/funpixel-world-and-story/references/world-kit.md +154 -0
- package/templates/common/.agents/skills/funpixel-3d/SKILL.md +89 -0
- package/templates/common/.agents/skills/funpixel-3d/references/bake-off.md +48 -0
- package/templates/common/.agents/skills/funpixel-3d/references/bench.md +103 -0
- package/templates/common/.agents/skills/funpixel-3d/references/budgets.md +48 -0
- package/templates/common/.agents/skills/funpixel-3d/references/characters.md +67 -0
- package/templates/common/.agents/skills/funpixel-3d/references/concept-image.md +47 -0
- package/templates/common/.agents/skills/funpixel-3d/references/engine-targets.md +31 -0
- package/templates/common/.agents/skills/funpixel-3d/references/hero-route.md +104 -0
- package/templates/common/.agents/skills/funpixel-3d/references/level-spec.md +109 -0
- package/templates/common/.agents/skills/funpixel-3d/references/levels.md +84 -0
- package/templates/common/.agents/skills/funpixel-3d/references/light-and-places.md +141 -0
- package/templates/common/.agents/skills/funpixel-3d/references/models.md +111 -0
- package/templates/common/.agents/skills/funpixel-3d/references/painted-target.md +184 -0
- package/templates/common/.agents/skills/funpixel-3d/references/reading.md +41 -0
- package/templates/common/.agents/skills/funpixel-3d/references/skin.md +85 -0
- package/templates/common/.agents/skills/funpixel-3d/references/skyboxes.md +65 -0
- package/templates/common/.agents/skills/funpixel-3d/references/textures.md +71 -0
- package/templates/common/.agents/skills/funpixel-3d/references/three-loading.md +199 -0
- package/templates/common/.agents/skills/funpixel-3d/references/unity.md +78 -0
- package/templates/common/.agents/skills/funpixel-art/SKILL.md +104 -0
- package/templates/common/.agents/skills/funpixel-art/references/2d-assets.md +108 -0
- package/templates/common/.agents/skills/funpixel-art/references/animation.md +126 -0
- package/templates/common/.agents/skills/funpixel-art/references/cast.md +73 -0
- package/templates/common/.agents/skills/funpixel-art/references/characters.md +49 -0
- package/templates/common/.agents/skills/funpixel-art/references/cost.md +107 -0
- package/templates/common/.agents/skills/funpixel-art/references/effects.md +85 -0
- package/templates/common/.agents/skills/funpixel-art/references/frames.md +87 -0
- package/templates/common/.agents/skills/funpixel-art/references/judging-frames.md +64 -0
- package/templates/common/.agents/skills/funpixel-art/references/look.md +102 -0
- package/templates/common/.agents/skills/funpixel-art/references/measure.md +119 -0
- package/templates/common/.agents/skills/funpixel-art/references/pipeline.md +185 -0
- package/templates/common/.agents/skills/funpixel-art/references/pixi.md +277 -0
- package/templates/common/.agents/skills/funpixel-art/references/planes.md +27 -0
- package/templates/common/.agents/skills/funpixel-art/references/prompts.md +171 -0
- package/templates/common/.agents/skills/funpixel-art/references/sheets.md +141 -0
- package/templates/common/.agents/skills/funpixel-art/references/style-file.md +39 -0
- package/templates/common/.agents/skills/funpixel-art/references/three.md +232 -0
- package/templates/common/.agents/skills/funpixel-art/references/ui.md +163 -0
- package/templates/common/.agents/skills/funpixel-art/references/unity.md +78 -0
- package/templates/common/.agents/skills/funpixel-art/references/wiring.md +41 -0
- package/templates/common/.agents/skills/funpixel-audio/SKILL.md +92 -0
- package/templates/common/.agents/skills/funpixel-audio/references/adaptive-music.md +204 -0
- package/templates/common/.agents/skills/funpixel-audio/references/engine.md +78 -0
- package/templates/common/.agents/skills/funpixel-audio/references/measure.md +101 -0
- package/templates/common/.agents/skills/funpixel-audio/references/unity.md +74 -0
- package/templates/common/.agents/skills/funpixel-feedback/SKILL.md +120 -0
- package/templates/common/.agents/skills/funpixel-feedback/references/memory.md +99 -0
- package/templates/common/.agents/skills/funpixel-game-feel/SKILL.md +104 -0
- package/templates/common/.agents/skills/funpixel-game-feel/references/contact.md +110 -0
- package/templates/common/.agents/skills/funpixel-game-feel/references/measure.md +125 -0
- package/templates/common/.agents/skills/funpixel-game-feel/references/motion.md +134 -0
- package/templates/common/.agents/skills/funpixel-game-feel/references/payoffs.md +35 -0
- package/templates/common/.agents/skills/funpixel-game-feel/references/recipes.md +107 -0
- package/templates/common/.agents/skills/funpixel-game-feel/references/unity.md +73 -0
- package/templates/common/.agents/skills/funpixel-game-logic/SKILL.md +113 -0
- package/templates/common/.agents/skills/funpixel-game-logic/references/balance.md +88 -0
- package/templates/common/.agents/skills/funpixel-game-logic/references/content-at-scale.md +147 -0
- package/templates/common/.agents/skills/funpixel-game-logic/references/depth.md +89 -0
- package/templates/common/.agents/skills/funpixel-game-logic/references/escalation.md +41 -0
- package/templates/common/.agents/skills/funpixel-game-logic/references/levels.md +61 -0
- package/templates/common/.agents/skills/funpixel-game-logic/references/probes.md +122 -0
- package/templates/common/.agents/skills/funpixel-game-logic/references/unity.md +108 -0
- package/templates/common/.agents/skills/funpixel-game-spine/SKILL.md +118 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/camera.md +94 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/multiplayer.md +101 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/patterns.md +103 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/performance.md +96 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/pixi.md +26 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/replay.md +102 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/room-api.md +63 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/room-modes.md +40 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/sdk.md +114 -0
- package/templates/common/.agents/skills/funpixel-game-spine/references/three.md +62 -0
- package/templates/common/.agents/skills/funpixel-gdd/SKILL.md +116 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/as-built.md +40 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/design-measures.md +24 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/design-note.md +88 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/experience.md +92 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/first-look.md +226 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/floors.md +62 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/frame.md +75 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/gameplay-contract.md +81 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/gdd-template.md +417 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-card.md +109 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-platformer.md +159 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-puzzle.md +145 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-racing.md +137 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-rpg.md +125 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-shooter.md +128 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-simulation.md +112 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-strategy.md +116 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/genre-survival.md +122 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/in-the-loop.md +106 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/invent-first.md +131 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/level-and-fun.md +112 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/pages.md +47 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/plan-at-size.md +140 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/production-tiers.md +117 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/sections.md +148 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/spec-folder.md +131 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/start.md +110 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/starters.md +175 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/the-path.md +133 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/track.md +29 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/world-and-identity.md +165 -0
- package/templates/common/.agents/skills/funpixel-gdd/references/worth.md +74 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/SKILL.md +107 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/references/contrast.md +123 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/references/hud-and-title.md +108 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/references/hud-genres.md +124 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/references/phone-check.md +156 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/references/recipes.md +279 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/references/restyle-ui.md +229 -0
- package/templates/common/.agents/skills/funpixel-mobile-ui/references/unity.md +61 -0
- package/templates/common/.agents/skills/funpixel-publish/SKILL.md +109 -0
- package/templates/common/.agents/skills/funpixel-publish/references/compose.md +113 -0
- package/templates/common/.agents/skills/funpixel-publish/references/motion.md +120 -0
- package/templates/common/.agents/skills/funpixel-publish/references/networks.md +75 -0
- package/templates/common/.agents/skills/funpixel-publish/references/other-hosts.md +65 -0
- package/templates/common/.agents/skills/funpixel-publish/references/playable-ad.md +90 -0
- package/templates/common/.agents/skills/funpixel-publish/references/server-build.md +85 -0
- package/templates/common/.agents/skills/funpixel-publish/references/store-shots.md +85 -0
- package/templates/common/.agents/skills/funpixel-publish/references/trailer.md +55 -0
- package/templates/common/.agents/skills/funpixel-publish/references/what-ships.md +30 -0
- package/templates/common/.agents/skills/funpixel-store/SKILL.md +81 -0
- package/templates/common/.agents/skills/funpixel-store/references/app-store.md +73 -0
- package/templates/common/.agents/skills/funpixel-store/references/google-play.md +141 -0
- package/templates/common/.agents/skills/funpixel-store/references/ios-release.md +63 -0
- package/templates/common/.agents/skills/funpixel-store/references/money.md +66 -0
- package/templates/common/.agents/skills/funpixel-store/references/purchases-and-review.md +74 -0
- package/templates/common/.agents/skills/funpixel-store/references/readiness-page.md +24 -0
- package/templates/common/.agents/skills/funpixel-store/references/readiness.md +107 -0
- package/templates/common/.agents/skills/funpixel-store/references/steam.md +38 -0
- package/templates/common/.agents/skills/funpixel-store/references/unity-store.md +55 -0
- package/templates/common/.agents/skills/funpixel-store/references/web-portals.md +43 -0
- package/templates/common/.agents/skills/funpixel-unity-game/SKILL.md +103 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/agent-ops.md +54 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/fixes.md +40 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/from-a-web-game.md +108 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/handing-off.md +42 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/performance.md +59 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/probe.md +80 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/shared-mac.md +121 -0
- package/templates/common/.agents/skills/funpixel-unity-game/references/traps.md +108 -0
- package/templates/common/.agents/skills/funpixel-verify/SKILL.md +109 -0
- package/templates/common/.agents/skills/funpixel-verify/references/actors.md +94 -0
- package/templates/common/.agents/skills/funpixel-verify/references/bar.md +62 -0
- package/templates/common/.agents/skills/funpixel-verify/references/checks.md +69 -0
- package/templates/common/.agents/skills/funpixel-verify/references/experience.md +67 -0
- package/templates/common/.agents/skills/funpixel-verify/references/eyes.md +54 -0
- package/templates/common/.agents/skills/funpixel-verify/references/fidelity.md +126 -0
- package/templates/common/.agents/skills/funpixel-verify/references/hit-bar.md +31 -0
- package/templates/common/.agents/skills/funpixel-verify/references/journeys.md +77 -0
- package/templates/common/.agents/skills/funpixel-verify/references/mobile-qa.md +123 -0
- package/templates/common/.agents/skills/funpixel-verify/references/playtest.md +161 -0
- package/templates/common/.agents/skills/funpixel-verify/references/review.md +105 -0
- package/templates/common/.agents/skills/funpixel-verify/references/rubric.md +67 -0
- package/templates/common/.agents/skills/funpixel-verify/references/scenes.md +49 -0
- package/templates/common/.agents/skills/funpixel-verify/references/unity.md +60 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/SKILL.md +99 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/atmosphere.md +208 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/forged-world.md +79 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/living-world.md +237 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/opening-and-chapters.md +215 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/shelf.md +80 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/story-at-size.md +159 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/voice.md +175 -0
- package/templates/common/.agents/skills/funpixel-world-and-story/references/world-kit.md +154 -0
- package/templates/common/.claude/settings.json +3 -0
- package/templates/common/.harnessignore +28 -0
- package/templates/common/AGENTS.md +135 -0
- package/templates/common/CLAUDE.md +1 -0
- package/templates/common/assets/assets.example.json +9 -0
- package/templates/common/index.html +13 -0
- package/templates/common/src/art/files.mjs +27 -0
- package/templates/common/src/assets.ts +62 -0
- package/templates/common/src/beat.ts +50 -0
- package/templates/common/src/characters.ts +54 -0
- package/templates/common/src/expose.ts +63 -0
- package/templates/common/src/feel.ts +78 -0
- package/templates/common/src/fpx-hooks.ts +46 -0
- package/templates/common/src/frame.ts +63 -0
- package/templates/common/src/grade.ts +47 -0
- package/templates/common/src/hud-kit.css +29 -0
- package/templates/common/src/hud-kit.tsx +85 -0
- package/templates/common/src/hud.tsx +55 -0
- package/templates/common/src/icons.tsx +35 -0
- package/templates/common/src/input.ts +70 -0
- package/templates/common/src/juice.ts +92 -0
- package/templates/common/src/levels.ts +43 -0
- package/templates/common/src/living.ts +91 -0
- package/templates/common/src/look.json +19 -0
- package/templates/common/src/look.ts +54 -0
- package/templates/common/src/main.tsx +31 -0
- package/templates/common/src/markers.ts +60 -0
- package/templates/common/src/motion-sample.ts +71 -0
- package/templates/common/src/motion.ts +92 -0
- package/templates/common/src/pin.ts +32 -0
- package/templates/common/src/platform.ts +67 -0
- package/templates/common/src/pose.ts +22 -0
- package/templates/common/src/probe-human.ts +29 -0
- package/templates/common/src/probe-kit.ts +99 -0
- package/templates/common/src/rules.ts +99 -0
- package/templates/common/src/save.ts +21 -0
- package/templates/common/src/screens.css +34 -0
- package/templates/common/src/screens.tsx +95 -0
- package/templates/common/src/selftests.ts +22 -0
- package/templates/common/src/session.ts +99 -0
- package/templates/common/src/sound.ts +85 -0
- package/templates/common/src/splash-funpixel/OFL.txt +94 -0
- package/templates/common/src/splash-funpixel/card.css +18 -0
- package/templates/common/src/splash-funpixel/card.tsx +22 -0
- package/templates/common/src/splash.css +15 -0
- package/templates/common/src/splash.tsx +86 -0
- package/templates/common/src/store.ts +68 -0
- package/templates/common/src/strike.ts +82 -0
- package/templates/common/src/styles.css +81 -0
- package/templates/common/src/toasts.ts +75 -0
- package/templates/common/src/world.ts +91 -0
- package/templates/common/test/feel.json +36 -0
- package/templates/common/test/platform.test.ts +42 -0
- package/templates/common/test/probes.ts +72 -0
- package/templates/common/test/rules.test.ts +88 -0
- package/templates/common/tsconfig.json +16 -0
- package/templates/common/vite.config.ts +6 -0
- package/templates/pixi/src/character.ts +63 -0
- package/templates/pixi/src/game.ts +99 -0
- package/templates/pixi/src/world-hud.ts +16 -0
- package/templates/spec/common/src/chapters.ts +97 -0
- package/templates/spec/common/src/levels.ts +68 -0
- package/templates/spec/common/src/probe-rooms.ts +63 -0
- package/templates/spec/common/src/room-map.tsx +30 -0
- package/templates/spec/common/src/tune.ts +93 -0
- package/templates/spec/common/test/rooms.test.ts +59 -0
- package/templates/spec/common/test/rooms.ts +53 -0
- package/templates/spec/three-world/test/rooms.ts +54 -0
- package/templates/three/src/character.ts +65 -0
- package/templates/three/src/game.ts +99 -0
- package/templates/three/src/world-hud.ts +22 -0
- package/templates/three-world/src/bot.ts +43 -0
- package/templates/three-world/src/budget.ts +73 -0
- package/templates/three-world/src/camera-rig.ts +99 -0
- package/templates/three-world/src/controls.tsx +49 -0
- package/templates/three-world/src/cutout-atlas.ts +26 -0
- package/templates/three-world/src/cutout-math.ts +47 -0
- package/templates/three-world/src/cutouts.ts +99 -0
- package/templates/three-world/src/dressing-math.ts +98 -0
- package/templates/three-world/src/dressing.ts +84 -0
- package/templates/three-world/src/expose.ts +82 -0
- package/templates/three-world/src/feel.ts +79 -0
- package/templates/three-world/src/game.ts +99 -0
- package/templates/three-world/src/hud.tsx +44 -0
- package/templates/three-world/src/icons.tsx +36 -0
- package/templates/three-world/src/input.ts +73 -0
- package/templates/three-world/src/juice.ts +91 -0
- package/templates/three-world/src/layout.ts +73 -0
- package/templates/three-world/src/light-math.ts +78 -0
- package/templates/three-world/src/light.ts +77 -0
- package/templates/three-world/src/look.json +30 -0
- package/templates/three-world/src/main.tsx +38 -0
- package/templates/three-world/src/models.ts +62 -0
- package/templates/three-world/src/palette.ts +21 -0
- package/templates/three-world/src/places.json +50 -0
- package/templates/three-world/src/places.ts +56 -0
- package/templates/three-world/src/pose.ts +22 -0
- package/templates/three-world/src/post.ts +47 -0
- package/templates/three-world/src/rest-math.ts +29 -0
- package/templates/three-world/src/rules.ts +89 -0
- package/templates/three-world/src/screens.css +45 -0
- package/templates/three-world/src/screens.tsx +96 -0
- package/templates/three-world/src/selftests.ts +25 -0
- package/templates/three-world/src/session.ts +99 -0
- package/templates/three-world/src/shots.ts +59 -0
- package/templates/three-world/src/sky.ts +48 -0
- package/templates/three-world/src/stage.ts +83 -0
- package/templates/three-world/src/styles.css +95 -0
- package/templates/three-world/src/surfaces.ts +55 -0
- package/templates/three-world/src/world.ts +70 -0
- package/templates/three-world/test/feel.json +29 -0
- package/templates/three-world/test/probes.ts +65 -0
- package/templates/three-world/test/resolve.mjs +13 -0
- package/templates/three-world/test/rules.test.ts +81 -0
- package/unity/com.funpixel.probe/Editor/Funpixel.Probe.Editor.asmdef +16 -0
- package/unity/com.funpixel.probe/Editor/Funpixel.Probe.Editor.asmdef.meta +7 -0
- package/unity/com.funpixel.probe/Editor/ProbeBuild.cs +97 -0
- package/unity/com.funpixel.probe/Editor/ProbeBuild.cs.meta +11 -0
- package/unity/com.funpixel.probe/Editor/ReleaseGuard.cs +23 -0
- package/unity/com.funpixel.probe/Editor/ReleaseGuard.cs.meta +11 -0
- package/unity/com.funpixel.probe/Editor.meta +8 -0
- package/unity/com.funpixel.probe/Runtime/Api/Funpixel.Probe.asmdef +14 -0
- package/unity/com.funpixel.probe/Runtime/Api/Funpixel.Probe.asmdef.meta +7 -0
- package/unity/com.funpixel.probe/Runtime/Api/FunpixelProbe.cs +146 -0
- package/unity/com.funpixel.probe/Runtime/Api/FunpixelProbe.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Api.meta +8 -0
- package/unity/com.funpixel.probe/Runtime/Player/Args.cs +87 -0
- package/unity/com.funpixel.probe/Runtime/Player/Args.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/Boot.cs +67 -0
- package/unity/com.funpixel.probe/Runtime/Player/Boot.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/Bridge.cs +559 -0
- package/unity/com.funpixel.probe/Runtime/Player/Bridge.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/Capture.cs +145 -0
- package/unity/com.funpixel.probe/Runtime/Player/Capture.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/Funpixel.Probe.Player.asmdef +32 -0
- package/unity/com.funpixel.probe/Runtime/Player/Funpixel.Probe.Player.asmdef.meta +7 -0
- package/unity/com.funpixel.probe/Runtime/Player/Inject.cs +127 -0
- package/unity/com.funpixel.probe/Runtime/Player/Inject.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/Json.cs +246 -0
- package/unity/com.funpixel.probe/Runtime/Player/Json.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/Probe.cs +194 -0
- package/unity/com.funpixel.probe/Runtime/Player/Probe.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/Stats.cs +266 -0
- package/unity/com.funpixel.probe/Runtime/Player/Stats.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player/UiTargets.cs +134 -0
- package/unity/com.funpixel.probe/Runtime/Player/UiTargets.cs.meta +11 -0
- package/unity/com.funpixel.probe/Runtime/Player.meta +8 -0
- package/unity/com.funpixel.probe/Runtime/link.xml +7 -0
- package/unity/com.funpixel.probe/Runtime/link.xml.meta +7 -0
- package/unity/com.funpixel.probe/Runtime.meta +8 -0
- package/unity/com.funpixel.probe/Tests/Editor/Funpixel.Probe.Tests.asmdef +25 -0
- package/unity/com.funpixel.probe/Tests/Editor/Funpixel.Probe.Tests.asmdef.meta +7 -0
- package/unity/com.funpixel.probe/Tests/Editor/StatsTests.cs +147 -0
- package/unity/com.funpixel.probe/Tests/Editor/StatsTests.cs.meta +11 -0
- package/unity/com.funpixel.probe/Tests/Editor/WireTests.cs +86 -0
- package/unity/com.funpixel.probe/Tests/Editor/WireTests.cs.meta +11 -0
- package/unity/com.funpixel.probe/Tests/Editor.meta +8 -0
- package/unity/com.funpixel.probe/Tests.meta +8 -0
- package/unity/com.funpixel.probe/package.json +14 -0
- package/unity/com.funpixel.probe/package.json.meta +7 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,2917 @@
|
|
|
1
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { verify, EXIT } from "./commands/verify.js";
|
|
4
|
+
import { init } from "./commands/init.js";
|
|
5
|
+
import { publish, TARGETS } from "./commands/publish.js";
|
|
6
|
+
import { store, STORE_SUBCOMMANDS } from "./commands/store.js";
|
|
7
|
+
import { doctor, doctorHost } from "./commands/doctor.js";
|
|
8
|
+
import { HOSTS } from "./doctor/host.js";
|
|
9
|
+
import { check } from "./commands/check.js";
|
|
10
|
+
import { discardOffers, upgrade, upgradeSelf } from "./commands/upgrade.js";
|
|
11
|
+
import { dev } from "./commands/dev.js";
|
|
12
|
+
import { parseBots } from "./relay/bots.js";
|
|
13
|
+
import { pack } from "./commands/pack.js";
|
|
14
|
+
import { exportCmd } from "./commands/export.js";
|
|
15
|
+
import { smoke } from "./commands/smoke.js";
|
|
16
|
+
import { test } from "./commands/test.js";
|
|
17
|
+
import { sim } from "./commands/sim.js";
|
|
18
|
+
import { isSimVerb, simulator } from "./commands/simulator.js";
|
|
19
|
+
import { unity } from "./commands/unity.js";
|
|
20
|
+
import { unityPlayer } from "./commands/unity-player.js";
|
|
21
|
+
import { unityBridge, unityEditor } from "./commands/unity-editor.js";
|
|
22
|
+
import { depth } from "./commands/depth.js";
|
|
23
|
+
import { reach } from "./commands/reach.js";
|
|
24
|
+
import { world, WORLD_ACTIONS } from "./commands/world.js";
|
|
25
|
+
import { life } from "./commands/life.js";
|
|
26
|
+
import { walk } from "./commands/walk.js";
|
|
27
|
+
import { compareTakes } from "./commands/compare-takes.js";
|
|
28
|
+
import { printFixes, score } from "./commands/score.js";
|
|
29
|
+
import { wow } from "./commands/wow.js";
|
|
30
|
+
import { framing } from "./commands/framing.js";
|
|
31
|
+
import { boxes } from "./commands/boxes.js";
|
|
32
|
+
import { places } from "./commands/places.js";
|
|
33
|
+
import { hook } from "./commands/hook.js";
|
|
34
|
+
import { feel } from "./commands/feel.js";
|
|
35
|
+
import { lookSheet } from "./commands/look-sheet.js";
|
|
36
|
+
import { gdd } from "./commands/gdd.js";
|
|
37
|
+
import { greenlight, GREENLIGHT_SUBCOMMANDS } from "./commands/greenlight.js";
|
|
38
|
+
import { browserLogin, login, logout, whoami } from "./commands/login.js";
|
|
39
|
+
import { budget, usage } from "./commands/account.js";
|
|
40
|
+
import { estimateCommand, gen } from "./commands/gen.js";
|
|
41
|
+
import { models, MODELS_SUBCOMMANDS } from "./commands/models.js";
|
|
42
|
+
import { assets, SUBCOMMANDS as ASSETS_SUBCOMMANDS } from "./commands/assets.js";
|
|
43
|
+
import { cast, CAST_SUBCOMMANDS } from "./commands/cast.js";
|
|
44
|
+
import { character } from "./commands/character.js";
|
|
45
|
+
import { job } from "./commands/job.js";
|
|
46
|
+
import { jobs } from "./commands/jobs.js";
|
|
47
|
+
import { optimize } from "./commands/optimize.js";
|
|
48
|
+
import { playable } from "./commands/playable.js";
|
|
49
|
+
import { reel, REEL_LIMITS } from "./commands/reel.js";
|
|
50
|
+
import { trailer, TRAILER_LIMITS } from "./commands/trailer.js";
|
|
51
|
+
import { formatsFor, SAFE } from "./trailer/placement.js";
|
|
52
|
+
import { model } from "./commands/model.js";
|
|
53
|
+
import { fidelity } from "./commands/fidelity.js";
|
|
54
|
+
import { auditScreens, BURST, capture, scene } from "./commands/look.js";
|
|
55
|
+
import { captureAll } from "./commands/capture-all.js";
|
|
56
|
+
import { compare } from "./commands/compare.js";
|
|
57
|
+
import { compareTarget, pairTargets } from "./commands/compare-target.js";
|
|
58
|
+
import { parts, parseGrid, parseKey } from "./commands/parts.js";
|
|
59
|
+
import { cutDefaults } from "./commands/cut.js";
|
|
60
|
+
import { lookRead } from "./commands/look-read.js";
|
|
61
|
+
import { look3d } from "./commands/look-3d.js";
|
|
62
|
+
import { HUD_SAMPLING, lookHud } from "./commands/look-hud.js";
|
|
63
|
+
import { lookTheme, THEME_SUBCOMMANDS } from "./commands/look-theme.js";
|
|
64
|
+
import { playtest, PLAYTEST_SUBCOMMANDS } from "./commands/playtest.js";
|
|
65
|
+
import { playtestHosted } from "./playtest/hosted.js";
|
|
66
|
+
import { mobileQa } from "./commands/mobile-qa.js";
|
|
67
|
+
import { lookFit } from "./commands/look-fit.js";
|
|
68
|
+
import { journey, JOURNEY_SUBCOMMANDS } from "./commands/journey.js";
|
|
69
|
+
import { profile, PROFILE_LIMITS } from "./commands/profile.js";
|
|
70
|
+
import { inspect } from "./commands/inspect.js";
|
|
71
|
+
import { phone } from "./commands/phone.js";
|
|
72
|
+
import { clip } from "./commands/clip.js";
|
|
73
|
+
import { judge, JUDGE_SUBCOMMANDS } from "./commands/judge.js";
|
|
74
|
+
import { lessons } from "./commands/lessons.js";
|
|
75
|
+
import { variety } from "./commands/variety.js";
|
|
76
|
+
import { pitch, PITCH_SUBCOMMANDS } from "./commands/pitch.js";
|
|
77
|
+
import { widen } from "./commands/widen.js";
|
|
78
|
+
import { loudness } from "./commands/loudness.js";
|
|
79
|
+
import { zero } from "./commands/zero.js";
|
|
80
|
+
import { review } from "./commands/review.js";
|
|
81
|
+
import { motion, MOTION_SUBCOMMANDS } from "./commands/motion.js";
|
|
82
|
+
import { compose, COMPOSE_SUBCOMMANDS } from "./commands/compose.js";
|
|
83
|
+
import { boardRun } from "./status/board.js";
|
|
84
|
+
import { beats } from "./commands/beats.js";
|
|
85
|
+
import { schema } from "./commands/schema.js";
|
|
86
|
+
import { loop, LOOP_LIMITS } from "./commands/loop.js";
|
|
87
|
+
import { directions } from "./commands/directions.js";
|
|
88
|
+
import { board, BOARD_SUBCOMMANDS } from "./commands/board.js";
|
|
89
|
+
import { feedback } from "./commands/feedback.js";
|
|
90
|
+
import { note, NOTE_SUBCOMMANDS } from "./commands/note.js";
|
|
91
|
+
import { owner, OWNER_SUBCOMMANDS } from "./commands/owner.js";
|
|
92
|
+
import { firstlook, FIRSTLOOK_SUBCOMMANDS } from "./commands/firstlook.js";
|
|
93
|
+
import { flow, FLOW_SUBCOMMANDS } from "./commands/flow.js";
|
|
94
|
+
import { spec, SPEC_SUBCOMMANDS } from "./commands/spec.js";
|
|
95
|
+
import { roomsCommand } from "./commands/rooms.js";
|
|
96
|
+
import { lanes, LANES_SUBCOMMANDS } from "./commands/lanes.js";
|
|
97
|
+
import { pick, PICK_SUBCOMMANDS } from "./commands/pick.js";
|
|
98
|
+
import { progress, PROGRESS_SUBCOMMANDS } from "./commands/progress.js";
|
|
99
|
+
import { journal } from "./commands/journal.js";
|
|
100
|
+
import { decide } from "./commands/decide.js";
|
|
101
|
+
import { DEFAULTS as PLAY_DEFAULTS, play, PLAY_SUBCOMMANDS } from "./commands/play.js";
|
|
102
|
+
import { STEP_LIMITS as PLAY_STEP_LIMITS } from "./play/steps.js";
|
|
103
|
+
import { FRONT_DOOR } from "./look/look.js";
|
|
104
|
+
import { GEN_FLAGS } from "./gen/kinds.js";
|
|
105
|
+
import { ENGINES, VIEWPORT_CHOICES } from "./verify/browser.js";
|
|
106
|
+
import { ui } from "./ui.js";
|
|
107
|
+
import { versionLine } from "./update.js";
|
|
108
|
+
export const USAGE = `usage:
|
|
109
|
+
funtools init [dir] [--framework pixi|three-world|three] [--name "Title"] [--idea "one line"] [--bare] [--offline] [--no-git]
|
|
110
|
+
funtools init "<one line: the game you want>" [--framework pixi|three-world|three] [--name "Title"] [--offline] [--no-git]
|
|
111
|
+
funtools dev [dir] [--no-devtools] [--relay [--bots 1-15] [--no-hold]] [--phone [--host address]] [--open]
|
|
112
|
+
funtools serve [dir] [--port 5794] [--host address] [--no-compress]
|
|
113
|
+
funtools open <url|file>
|
|
114
|
+
funtools find "<what you want to do>" [--json]
|
|
115
|
+
funtools mcp
|
|
116
|
+
funtools setup [--host claude|cursor|codex|kiro|robin|all] [--dry-run] [--json]
|
|
117
|
+
funtools doctor [dir] [--add-hooks]
|
|
118
|
+
funtools doctor --host [claude|codex|cursor|kiro] [dir] [--json]
|
|
119
|
+
funtools upgrade [dir] [--discard] | upgrade --self
|
|
120
|
+
funtools smoke [dir] [--json]
|
|
121
|
+
funtools smoke-build <fresh-folder> --plan <folder with design/> [--json]
|
|
122
|
+
funtools test [dir] [--node] [--goals] [--json]
|
|
123
|
+
funtools check [dir] [--viewport desktop|phone|both] [--phone] [--front-door | --click selector] [--no-test] [--no-idle] [--world | --no-world] [--json]
|
|
124
|
+
funtools sim [dir] [--script test/probes.ts] [--timeout 300] [--json]
|
|
125
|
+
funtools sim lease [--device "iPhone 17 Pro"] [--runtime "iOS 26.5"] [--no-boot] [--owner pid] [--json] | sim release <udid> [--force]
|
|
126
|
+
| sim list [--json] | sim gc [--dry-run] [--json]
|
|
127
|
+
| sim shot <udid> [--orientation portrait|landscape|landscape-left|landscape-right] [--app bundle-id] [--out file] [--json]
|
|
128
|
+
| sim tap <udid> x y [--points] [--orientation …] [--app bundle-id] [--tool baguette|idb] [--json]
|
|
129
|
+
funtools unity run [--project dir] [--unity bin] [--log file] [--no-quit] [--max 2] [--stale-minutes 20] [--max-wait minutes] -- <unity args>
|
|
130
|
+
| unity jobs [--json] | unity gc [roots…] [--days 14] [--yes] [--json]
|
|
131
|
+
| unity gc --disk [roots…] [--below 20] [--days 2] [--only worktrees,target,derived-data,unity,scratch,context] [--apply] [--strict] [--json]
|
|
132
|
+
| unity probe [--project dir] [--target mac|sim] [--path script.json] [--seconds N] [--tier phone|tablet|mac] [--budget file|json]
|
|
133
|
+
[--player App.app] [--rebuild] [--no-build] [--port N] [--udid U] [--device name] [--visible] [--sound] [--out file] [--json]
|
|
134
|
+
[--player-args file] [-- <the game's own launch arguments>] (probe, play and capture alike)
|
|
135
|
+
[--strict] (the kit's starting points judge as budgets: a release gate; without it only --budget's numbers fail)
|
|
136
|
+
| unity probe install [--project dir] [--from folder | --git url --ref ref] [--absolute] [--tests] [--force] [--dry-run] | unity probe uninstall
|
|
137
|
+
| unity play start|end|status | play tap x y|<name> | drag x1 y1 x2 y2 [ms] | key <name> | pad <button> | state | ui | views
|
|
138
|
+
| view <name> | scene <name> | shot [--out f.png] | record [--seconds 5] [--out dir] | act "<step>" … | send '<json>' [--points] [--mouse]
|
|
139
|
+
| unity capture --all [--project dir] [--target mac|sim] [--out dir] [--settle-ms 1000] [--dev] [--json]
|
|
140
|
+
| unity bridge status|install|remove [--project dir]
|
|
141
|
+
| unity editor console|compile|play|stop|shot|menu|call|hierarchy|add|save|open|close [--project dir] [--open]
|
|
142
|
+
funtools spec [lint] [dir] [--spec design/spec] [--tier mid|deep] [--json] | spec init [dir] [--tier mid|deep] [--json]
|
|
143
|
+
funtools rooms [dir] [--script test/rooms.ts] [--timeout 300] [--json] | rooms open <room> [dir] [--by owner] ["words"] [--json]
|
|
144
|
+
| rooms plan [dir] [--by owner] [--json]
|
|
145
|
+
funtools depth [dir] [--out dir] [--place field] [--tension field] [--progress field] [--fill] [--json]
|
|
146
|
+
funtools reach [dir] [--level file,…] [--json]
|
|
147
|
+
funtools world forge [--seed N] [--rooms N] [--kind 2d|3d] [--from GDD.md|brief.json] [--variety N] [--force]
|
|
148
|
+
[--dir game] [--json] | world check [--dir game] [--json]
|
|
149
|
+
funtools feel [dir] [--script test/feel.json | --journey name] [--no-sweep] [--engine chromium|webkit] [--json]
|
|
150
|
+
funtools gdd [lint] [dir|file.md] [--sources] [--strict] [--json] | gdd page [dir] [--json]
|
|
151
|
+
funtools greenlight [dir] | greenlight page
|
|
152
|
+
| greenlight lock --version v1.0 [--override "why"] [--dir game] [--json]
|
|
153
|
+
| greenlight worth [dir] [--target <min> --by owner] | greenlight worth choose <option> --why "…"
|
|
154
|
+
funtools verify [dir|url] [--viewport desktop|phone|both] [--watch] [--checks file.json]
|
|
155
|
+
[--manifest funpixel.json] [--no-sweep] [--front-door | --click selector]
|
|
156
|
+
[--engine chromium|webkit] [--json | --report]
|
|
157
|
+
funtools verify [dir] --players 2 [--json]
|
|
158
|
+
funtools capture [dir|url] [--view name | --all] [--verbs a,b] [--hold ms] [--burst frames]
|
|
159
|
+
[--viewport desktop|phone [--orientation portrait|landscape] [--safari]]
|
|
160
|
+
[--front-door | --click selector] [--out file.png] [--engine chromium|webkit]
|
|
161
|
+
[--seed n | --free-running] [--json]
|
|
162
|
+
funtools compare [dir] [--baseline] [--json]
|
|
163
|
+
funtools compare [dir|url] --target picture[,picture…] [--view name[,name…]] [--viewport phone|desktop|both]
|
|
164
|
+
[--fit contain|cover] [--verbs a,b] [--hold ms] [--front-door | --click selector] [--out dir|file.png]
|
|
165
|
+
[--seed n | --free-running] [--json]
|
|
166
|
+
funtools parts <sheet.png|jpg|webp> [--grid CxR | --boxes boxes.json] [--names a,b,c] [--key auto|none|paper|#rrggbb]
|
|
167
|
+
[--pivot bottom|top|center] [--pad 2] [--merge 6] [--clean [--keep-shadows]] [--webp] [--out dir] [--json]
|
|
168
|
+
funtools cut <sheet.png|jpg|webp> [--grid CxR | --boxes boxes.json] [--names a,b,c] [--keep-shadows]
|
|
169
|
+
[--pivot bottom|top|center] [--pad 2] [--merge 6] [--webp] [--out dir] [--json]
|
|
170
|
+
funtools audit-screens [dir|url] [--views a,b] [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
171
|
+
[--front-door | --click selector] [--json]
|
|
172
|
+
funtools scene [dir|url] [--view name] [--verbs a,b] [--hold ms] [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
173
|
+
[--motion] [--front-door | --click selector] [--json]
|
|
174
|
+
funtools hook [dir|url] [--seconds n] [--verbs a,b | --idle] [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
175
|
+
funtools life [dir|url] [--view name] [--moment name] [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
176
|
+
[--front-door | --click selector] [--json]
|
|
177
|
+
funtools walk [dir|url] [--route name,… | --chain a,b] [--seconds n] [--no-film] [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
178
|
+
[--json]
|
|
179
|
+
funtools score [dir|url] [--target picture --view name [--hero id] [--hero-share 0.2] [--capture shot.png]]
|
|
180
|
+
[--viewport desktop|phone] [--no-judge] [--seed n | --free-running] [--json]
|
|
181
|
+
funtools score [dir] --fixes [--json]
|
|
182
|
+
funtools framing [dir|url] [--actors id,…] [--view name] [--viewport desktop|phone] [--json]
|
|
183
|
+
funtools boxes [dir|url] [--view name,…] [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
184
|
+
[--front-door | --click selector] [--json]
|
|
185
|
+
funtools places [dir|url] [--view name,…] [--viewport desktop|phone] [--json]
|
|
186
|
+
funtools compare-takes <takeA> <takeB> [--names A,B] [--route name,…] [--viewport desktop|phone]
|
|
187
|
+
[--seconds n] [--no-score] [--out dir] [--json]
|
|
188
|
+
funtools profile [dir|url] [--views a,b] [--viewport desktop|phone] [--seed n] [--frames 120]
|
|
189
|
+
[--settle ms] [--software] [--throttle 4] [--repeat 3] [--against profile.json]
|
|
190
|
+
[--compare [--budget 18]] [--front-door | --click selector] [--json]
|
|
191
|
+
funtools inspect [dir] [--at x,y | --name label] [--tree] [--url http://localhost:5317] [--view name]
|
|
192
|
+
[--verbs a,b] [--settle ms] [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
193
|
+
[--front-door | --click selector] [--json]
|
|
194
|
+
funtools clip [dir] [--frames dir] [--fps 12] [--seats player,animator] [--json] | clip report [--dir game] [--json]
|
|
195
|
+
funtools judge structure <picture|folder…> [--prompt "what it should be"] [--dir game] [--json]
|
|
196
|
+
funtools judge identity <model.glb | view pictures|folder…> [--concept concept.png] [--medium model|sprites]
|
|
197
|
+
[--dir game] [--json] | judge report [n] [--dir game] [--json]
|
|
198
|
+
funtools phone [dir|url] [--sim "iPhone 17 Pro"|udid] [--orientation portrait|landscape] [--seconds 6]
|
|
199
|
+
[--taps "25%,80% 75%,80%"] [--swipes "right up"] [--keep] [--out dir] [--json]
|
|
200
|
+
funtools look [dir|url|capture.png] --read [--view name] [--viewport desktop|phone] [--hold ms]
|
|
201
|
+
[--player regex] [--threats regex] [--colours player=#hex,threat=#hex]
|
|
202
|
+
[--front-door | --click selector] [--json]
|
|
203
|
+
funtools look [dir|url] --hud [--view name] [--verbs a,b] [--samples 8] [--every ms]
|
|
204
|
+
[--viewport desktop|phone [--orientation portrait|landscape]] [--front-door | --click selector] [--json]
|
|
205
|
+
funtools look [dir|url] --fit [--view name] [--front-door | --click selector] [--json]
|
|
206
|
+
funtools look [dir] --sheet [--sprites dir,file…] [--sizes 24,48,96] [--ground #hex] [--out file.png] [--json]
|
|
207
|
+
funtools look set <token=value …> [--dir game] [--json] | look patch <file.json> [--dir game] [--json] | look report [dir] [--json]
|
|
208
|
+
funtools play [dir|url] [--viewport desktop|phone [--orientation portrait|landscape]] [--rounds 3] [--long minutes] [--engine chromium|webkit] [--json]
|
|
209
|
+
funtools play act [--look] "<step>" … | play look | play note "<what you saw>" | play check <file.json|-> | play report <file.json|->
|
|
210
|
+
| play end | play status (each takes [--round N] [--dir game]: the round play printed;
|
|
211
|
+
needed when two rounds are open in one folder, e.g. a blind pair)
|
|
212
|
+
play report --round N --refile <file> file a corrected report into a closed round (ended or filed); the earlier one is kept
|
|
213
|
+
[--dir game] [--json]
|
|
214
|
+
funtools journey [--dir game] | journey record <name> [--viewport desktop|phone [--orientation portrait|landscape]]
|
|
215
|
+
[--seed n] [--seconds 120] [--force] | journey from-play <round|last> <name> [--seed n] [--force]
|
|
216
|
+
funtools journey replay <name> [--repeat 1-3] [--against run] [--dir game] [--engine chromium|webkit] [--json] | journey diff <name> [a] [b] [--json]
|
|
217
|
+
funtools playtest [dir] [--quick] [--versus <evidence dir>|none] [--no-build] [--long minutes] [--phone [--sim name]] [--hosted [--yes]] [--json]
|
|
218
|
+
funtools playtest experience | report | status [--dir game] [--cost-usd n] [--min-score 6.5] [--json]
|
|
219
|
+
funtools playtest keep [--dir game] [--drop K<n> --by owner "<their words>"] [--json]
|
|
220
|
+
funtools mobile-qa [dir] [--phone [--sim name|udid]] [--no-build] [--json] | mobile-qa report [--dir game] [--json]
|
|
221
|
+
funtools lessons [--genre g] [--dir game] [--json]
|
|
222
|
+
funtools lessons add "<finding>" --fix "<what fixed it>" [--source panel|player|verify|look]
|
|
223
|
+
[--delta 0.4] [--share] [--dir game] [--json]
|
|
224
|
+
funtools lessons motif [--dir game] [--json]
|
|
225
|
+
funtools variety [dir] [--within] [--json]
|
|
226
|
+
funtools wow [dir] [--genre card] [--json] | wow pick <move-id> [--signature] [--expression "…"] [--why "…"] [--check "event name"|"frame view"] [--dir d] | wow check [dir]
|
|
227
|
+
funtools pitch [show|propose|pick|ask|lock] [<choice> <option[,option]>] [--why "…"] [--label "…"] [--picture path]
|
|
228
|
+
[--cost "…"] [--note "the person's words"] [--dir game] [--json]
|
|
229
|
+
funtools pitch widen [--seed N] [--dial tight|open|wide] [--brief "…"] | widen score [--text "…"] | widen keep <card>
|
|
230
|
+
[--dir game] [--json]
|
|
231
|
+
funtools loudness [files or folders…] [--dir game] [--json]
|
|
232
|
+
funtools beats [tracks or folders…] [--meter 4] [--dir game] [--json]
|
|
233
|
+
funtools schema [dir] [--strict] [--json]
|
|
234
|
+
funtools loop <video|frames folder> [--fps 12] [--min 6] [--max 24] [--size 256] [--key auto|none|#rrggbb]
|
|
235
|
+
[--name walk] [--out file.png] [--dir game] [--json]
|
|
236
|
+
funtools directions <render folder> [--dirs 8|4] [--size 128] [--key auto|none|#rrggbb] [--reverse]
|
|
237
|
+
[--name hero] [--out file.png] [--dir game] [--json]
|
|
238
|
+
funtools board [show|search "<words>"|wander <ref>|add <link|file>] [--keep 1,3] [--lends palette,light]
|
|
239
|
+
[--note "…"] [--licence "…"] [--by name] [--title "…"] [--limit 12] [--any-licence]
|
|
240
|
+
[--dir game] [--json]
|
|
241
|
+
funtools journal ["<what you changed>" [--why "<why>"] [--by name] [--files a,b] [--values k=v,k=v]]
|
|
242
|
+
[--dir game] [--json]
|
|
243
|
+
funtools decide ["<choice>" --because "<reason>" [--over "<alternative>"] [--supersedes D3]]
|
|
244
|
+
| decide --from N<n> ["<the decision>"] [--touches look,src/hud.tsx] [--dir game] [--json]
|
|
245
|
+
funtools lanes [status] | lanes plan [--stage s] [--lanes play,look,audio|room-2|rooms] [--cap usd] [--timebox min]
|
|
246
|
+
| lanes brief|start|done|gate|sync|merge <id> | lanes start --all [--by name]
|
|
247
|
+
| lanes checkpoint <id> --line "…" [--shot png] [--cost usd] [--minutes n] [--final]
|
|
248
|
+
| lanes verdict <id> pass|fail --by <seat> ["words"] | lanes report | lanes cap <id> <usd> --by owner
|
|
249
|
+
| lanes drop <id> --by owner [--dir game] [--json]
|
|
250
|
+
funtools pick [list] | pick <topic> <a.png> <b.png> [c.png] [--lane id] [--question "…"]
|
|
251
|
+
| pick choose <topic|Q> <option> --by owner ["words"] [--dir game] [--json]
|
|
252
|
+
funtools status [dir] [--json] (flow's status: where the run is and the one next step)
|
|
253
|
+
funtools zero [dir] [--json] (the zero board: errors, overlaps, seams, frame time, round by round)
|
|
254
|
+
funtools review [file|folder…] [--dir project] [--loop] [--still] [--freeze s] [--triangles n]
|
|
255
|
+
[--size WxH] [--alpha] [--lufs n] [--json] (the zero board for any trade's media)
|
|
256
|
+
funtools flow [dir] | flow start [dir] --idea "<the game in a line>" [--autonomy plan-then-go|ask|go] [--budget <credits>|open|free|code-drawn] [--rounds n]
|
|
257
|
+
[--framework f] [--name t] [--bare] [--offline] [--no-git] | flow ask [dir]
|
|
258
|
+
| flow agree [dir] --by owner ["words"] | flow round [dir] | flow look [dir] | flow drop [dir] --by owner "<the owner's words>" [--json]
|
|
259
|
+
funtools note [list] | note ["<the owner's words>"] | note add "<words>" [--stage <stage>] [--screenshot path] [--by owner|agent] [--hold]
|
|
260
|
+
| note close <N> --evidence <path|sha|test> | note close <N> --by owner --force ["words"] | note verdict <N|stage> accept|reject ["words"]
|
|
261
|
+
[--dir game] [--json]
|
|
262
|
+
funtools owner [list] [dir] | owner add "<line>" [--repo dir] | owner remove <n|id> [--repo dir] | owner seed
|
|
263
|
+
| owner apply <n|id> [--dir game] | owner skip <n|id> ["why"] [--dir game] [--json]
|
|
264
|
+
funtools firstlook [dir] | firstlook skip <item> --by owner "<their words>" | firstlook need <item>
|
|
265
|
+
| firstlook render [dir] [--page design/first-look/draw.html] [--dir game] [--json]
|
|
266
|
+
funtools progress [status] | progress card --line "<what changed>" [--stage s] [--shot path|last] [--clip path]
|
|
267
|
+
[--next "…"] [--spent n] [--evidence verify.json|file] [--decision "a; b"] [--out "a; b"] [--standin "a; b"]
|
|
268
|
+
| progress heartbeat --tried "…" [--shot path|last] [--stage s]
|
|
269
|
+
| progress ask "<question>" --kind taste|spend|scope --options a,b [--default a] [--timeout 10m]
|
|
270
|
+
| progress answer <Q> <option> ["words"] | progress wait [Q] [--for 15m] | progress report
|
|
271
|
+
[--dir game] [--json]
|
|
272
|
+
funtools feedback collect <folder…> [--out report.md] [--propose] [--html page.html] [--json]
|
|
273
|
+
funtools feedback log "<what happened>" [--kind step|test|dead-end|lesson|decision|worked|didnt|owner]
|
|
274
|
+
[--metric name --before n --after n --unit u] [--evidence path] [--film path] [--sha commit]
|
|
275
|
+
[--test name] [--cost-usd n] [--minutes n] [--minutes-lost n] [--tool name] [--stage s]
|
|
276
|
+
[--verdict pick|reject|note] [--screenshot path] [--dir game] [--json]
|
|
277
|
+
funtools feedback ab "<game>" --routes a,b [--overall 7.0,8.0] [--axes "story=7.0/8.7,…"] [--winner b]
|
|
278
|
+
[--judges "3 blind seats"] [--what "…"] [--evidence path] [--dir game] [--json]
|
|
279
|
+
funtools feedback merged [--sha ref] [--dir game] [--json]
|
|
280
|
+
funtools pack [dir] [--dry-run] [--no-build] [--json] [--out file.tar.gz]
|
|
281
|
+
funtools export <dest> [--dir game] [--check] [--no-browser] [--strip-metadata] [--json]
|
|
282
|
+
funtools gen <image|texture|strip|sprite|edit|sheet|sound|music|model3d|skybox|find-material|material|video|
|
|
283
|
+
inspect|optimize-model|fracture|lod|rig|render|finalize|level|skin|retexture|motion|retarget>
|
|
284
|
+
["prompt"] [options]
|
|
285
|
+
[--out path | --unity <project>]
|
|
286
|
+
[--cast <id>] [--no-wait] [--yes] [--strip-title] [--json]
|
|
287
|
+
funtools character list [--medium 2d|3d] [--body biped|quadruped|flyer] [--json]
|
|
288
|
+
funtools character get <id> [--files a,b] [--out dir] [--json]
|
|
289
|
+
funtools character restyle <id> "the new look" [--game-id id] [--animations idle,walk]
|
|
290
|
+
[--palette #hex,…] [--quality low|medium|high] [--yes] [--json]
|
|
291
|
+
funtools estimate <kind> ["prompt"] [options] [--json]
|
|
292
|
+
funtools models list [--json] | models install <local id> [--force] [--json]
|
|
293
|
+
funtools assets status [--dir game] [--json]
|
|
294
|
+
funtools assets gen [--only a,b] [--family f] [--stale] [--max-credits n] [--batch n] [--dry-run] [--yes]
|
|
295
|
+
[--dir game] [--json]
|
|
296
|
+
funtools assets regen <name> --because "what was wrong" [--dry-run] [--yes] [--dir game] [--json]
|
|
297
|
+
funtools assets process [--only a,b] [--dir game] [--json]
|
|
298
|
+
funtools assets pixels [file.png…] [--snap] [--dir game] [--json]
|
|
299
|
+
funtools assets script [--write] [--from design/GDD.md] [--dir game] [--json]
|
|
300
|
+
funtools assets budget [<credits>] [--by owner] [--dir game] [--json]
|
|
301
|
+
funtools cast add <id> --master <png> --pins "the design in words" [--turnaround <png>] [--name N]
|
|
302
|
+
[--kind character|creature|structure|vehicle|prop] [--features "a;b"] [--may-change "…"]
|
|
303
|
+
[--moves] [--by owner] [--dir game] [--json]
|
|
304
|
+
funtools cast list [--dir game] [--json]
|
|
305
|
+
funtools cast check <id> <picture…> [--mode same|pose] [--dir game] [--json]
|
|
306
|
+
funtools job <id> [--wait] [--out path] [--json]
|
|
307
|
+
funtools jobs [--tool name] [--status queued|running|succeeded|failed|cancelled] [--limit 1-100]
|
|
308
|
+
[--cursor c] [--refusals] [--json]
|
|
309
|
+
funtools optimize [dir] [--check] [--json]
|
|
310
|
+
funtools optimize <compress|pack-atlas|unpack-atlas|pack-strip|optimize-mesh> <path…>
|
|
311
|
+
[--dir game] [--max-size px] [--format keep|png|jpeg] [--quantize]
|
|
312
|
+
[--padding px] [--pot] [--no-trim] [--frame-size px] [--out path]
|
|
313
|
+
[--out-dir dir] [--levels phone,desktop,high] [--class character|prop|set_piece|…]
|
|
314
|
+
[--write] [--json]
|
|
315
|
+
funtools playable [dir] --network applovin|unity|ironsource|mintegral|google|meta|tiktok
|
|
316
|
+
[--no-play] [--json]
|
|
317
|
+
funtools trailer [dir|url] [--seconds 30] [--fps 15] [--formats 16x9,9x16,1x1,4x5 | --for tiktok,feed,store]
|
|
318
|
+
[--viewport desktop|phone]
|
|
319
|
+
[--music file | --no-music] [--no-sfx] [--title "Name"] [--key-art file] [--logo file]
|
|
320
|
+
[--cta "Play free"] [--intro clip.mp4] [--outro clip.mp4] [--out dir] [--json]
|
|
321
|
+
funtools reel [dir|url] [--seconds 12] [--fps 12] [--viewport desktop|phone]
|
|
322
|
+
[--frames-only] [--out file.mp4] [--json]
|
|
323
|
+
funtools motion render <scene.html|folder> [--fps 30|60] [--seconds n | --frames n]
|
|
324
|
+
[--size 1080x1920 | --aspect 9:16|16:9|1:1|4:5] [--out dir] [--webm] [--audio file]
|
|
325
|
+
[--prove] [--seed n] [--json]
|
|
326
|
+
| motion check <render folder|scene> [--prove] [--json] | motion new <name> [--aspect 9:16] [--dir folder]
|
|
327
|
+
funtools compose new <trailer|post|endcard> [dir] [--name n] [--aspect 16:9|9:16|1:1] [--seconds n] [--force] [--json]
|
|
328
|
+
funtools compose <check|render> <composition.html> [--dir game] [--aspect 16:9|9:16|1:1] [--seconds n] [--fps 30]
|
|
329
|
+
[--quality draft|standard|high] [--software] [--out file.mp4] [--json]
|
|
330
|
+
funtools compose variations [design/compose/ads.json] [--dir game] [--max 12] [--dry-run] [--fps 30]
|
|
331
|
+
[--quality draft|standard|high] [--software] [--out dir] [--json]
|
|
332
|
+
funtools model <inspect|render|finalize|skin> <asset-id|model file gen saved> [--frames 1-12]
|
|
333
|
+
[--size px] [--keep-clips Idle,Walk] [--tone #rrggbb] [--out path] [--json]
|
|
334
|
+
funtools model retexture <asset-id|model file> "new look" [--palette #a,#b] [--game-id id]
|
|
335
|
+
[--quality low|medium|high] [--roughness] [--normal] [--out path] [--json]
|
|
336
|
+
funtools model level <spec.json> [--size 256-1024] [--out path] [--json]
|
|
337
|
+
funtools model --local <inspect|normalize|decimate|render|finalize> <model.glb|gltf|fbx>
|
|
338
|
+
[--out path] [--target-triangles n] [--height-m m] [--frames 1-12]
|
|
339
|
+
[--size px] [--keep-clips Idle,Walk] [--json]
|
|
340
|
+
funtools model --local level <spec.json> [--out file.glb] [--size 256-1024] [--json]
|
|
341
|
+
funtools model clips <model.glb|gltf> [--json]
|
|
342
|
+
funtools fidelity <model.glb> name=ref.png [name=ref.png …] [--renders dir] [--out dir] [--size px]
|
|
343
|
+
[--own-axes] [--roll] [--outline] [--quarter-turns] [--same-scale] [--min pct] [--json]
|
|
344
|
+
funtools login [--key fp_live_…|-] [--api <url>] [--scope a,b]
|
|
345
|
+
funtools whoami
|
|
346
|
+
funtools --version | -v | version
|
|
347
|
+
funtools usage [--days 1-92] [--this-key] [--json]
|
|
348
|
+
funtools budget [--json]
|
|
349
|
+
funtools logout
|
|
350
|
+
funtools publish [dir] [--target funpixel|itch|pages|folder] [--yes] [--json]
|
|
351
|
+
funpixel: [--name "Title"] [--game-id id]
|
|
352
|
+
itch: --itch user/game[:channel] pages: [--project name] folder: --out dir
|
|
353
|
+
funtools store init|doctor|lint [dir] [--store play|ios|steam] [--json] | store plan [dir] [--store play|ios] [--json]
|
|
354
|
+
|
|
355
|
+
init write a new game the platform builds as-is, with its agent files; with an
|
|
356
|
+
API key it also mints the game's id on the server (--offline skips that).
|
|
357
|
+
One line (--idea, or a first argument with spaces) picks the closest starter,
|
|
358
|
+
each pick with its reason: the engine (3D words → Three), the genre recipe
|
|
359
|
+
(the GDK's cards), how a phone holds it, a folder and a working title; it seeds
|
|
360
|
+
design/GDD.md with the line as the pitch. Flags and your edits override all of it.
|
|
361
|
+
Into a folder with an owner's design/plan.md: the plan sizes the tier, the planning
|
|
362
|
+
stages it answers start done, nothing in design/ is overwritten. --bare writes the
|
|
363
|
+
spine only (hooks, safe band, platform, save, sound, store, look, probes) and a
|
|
364
|
+
game.ts that opens the engine: no demo round to move aside. The new game is its own
|
|
365
|
+
git repository with a first commit (skipped inside an existing repository;
|
|
366
|
+
--no-git skips it): push it to a remote so it survives this machine
|
|
367
|
+
setup the kit into your agent in one command: the package's plugin builds copied to
|
|
368
|
+
~/.funpixel/plugins/<version>, then installed into each host found (or the one named): Claude Code
|
|
369
|
+
(its plugin marketplace and install), Cursor (its local plugins folder), Kiro (the installer), and
|
|
370
|
+
the one step Codex and Robin take in their apps. --dry-run says every step and runs none. Never
|
|
371
|
+
writes a key: funtools login is next
|
|
372
|
+
exit 0 set up · 1 a step failed or no host found · 3 error
|
|
373
|
+
mcp funtools as a local MCP server over stdio, for a host with no shell (Robin, a desktop chat app): the
|
|
374
|
+
path as tools (flow, find, flow_start, flow_ask, flow_agree, flow_round, flow_look, check, verify, test, sim, capture,
|
|
375
|
+
compare, score, the asset steps, gen, notes, progress_card, reel, compose_video, ad_variations), dev (starts the preview, returns its
|
|
376
|
+
address), image (a picture into the chat) and run (the rest of the toolbox). Never publish, login or
|
|
377
|
+
logout: the person runs those. Started by the host: command funtools, arguments mcp
|
|
378
|
+
find search the kit for an ask in plain words: the three skills to read and the five commands that fit
|
|
379
|
+
best, each with the words that matched. Read-only and offline
|
|
380
|
+
exit 0 found · 2 nothing matched · 3 error
|
|
381
|
+
serve a Unity WebGL build (a folder with index.html, or a Unity project's Builds/WebGL) on this Mac
|
|
382
|
+
and on a phone on the same Wi-Fi, with its LAN address and a QR code: Brotli files go as
|
|
383
|
+
Brotli only to a browser that accepts it on localhost or https, and as gzip made from the
|
|
384
|
+
same bytes to anything else (a phone on http://<LAN address> refuses Brotli:
|
|
385
|
+
ERR_CONTENT_DECODING_FAILED); .gz as gzip; .wasm as application/wasm, .data as
|
|
386
|
+
application/octet-stream. Plain compressible files (Addressables bundles, .wasm/.json/.data
|
|
387
|
+
with no .br/.gz twin, .js, .css, .html, .svg, .txt, everything in StreamingAssets/aa but
|
|
388
|
+
media) are compressed on the fly as a CDN would, Brotli or gzip by the same rule, so what a
|
|
389
|
+
page fetches here weighs what it would live; never images, audio or video; a Range request
|
|
390
|
+
gets the bytes as stored. --no-compress sends files as stored (the raw weight). Binds every
|
|
391
|
+
interface (--host 127.0.0.1 keeps it on this Mac); runs until Ctrl-C, then says what it sent
|
|
392
|
+
exit 0 stopped · 3 no build there, or the port is taken
|
|
393
|
+
open open a URL or a file (a target frame, a clip) on the owner's screen with the system's opener; a host with
|
|
394
|
+
its own browser (Cursor, Codex) shows it there instead. Nothing opens in CI or with FUNPIXEL_NO_OPEN
|
|
395
|
+
exit 0 opened · 2 nothing opens here · 3 error
|
|
396
|
+
dev the game's Vite dev server, with the platform's inspector and profiler; --open opens the preview
|
|
397
|
+
in the owner's browser, in its own tab, once it is up;
|
|
398
|
+
--relay also runs a local relay: open /__funpixel/play in two tabs to play
|
|
399
|
+
the game's dev room (needs the funpixel-relay binary and a Redis);
|
|
400
|
+
--bots N seats N headless bot players there first, each playing through the
|
|
401
|
+
game's __FPX_VERBS__ (else its __FPX_KEYS__, else taps) until you stop dev;
|
|
402
|
+
a full reload is held while two or more pages are in the room, so a save never
|
|
403
|
+
ends the match being tested (--no-hold sends each at once);
|
|
404
|
+
--phone serves it on this Mac's local-network address and prints a QR code: the
|
|
405
|
+
game on your real phone, rebuilt live (off by default: it exposes the dev server
|
|
406
|
+
to the network; --host 0.0.0.0 binds every interface)
|
|
407
|
+
doctor static checks: will the platform build it, can verify measure it;
|
|
408
|
+
a Unity project gets the Unity pre-flight instead (run it at project start and before
|
|
409
|
+
every build; it reads files, never starts Unity): Git LFS pointer files where binaries
|
|
410
|
+
belong (Assets, Packages, every local file: package, Library/PackageCache) with the
|
|
411
|
+
repository to run \`git lfs pull\` in; activeInputHandler against an iOS or Android
|
|
412
|
+
target; TextMeshPro's Essential Resources; a camera with no AudioListener; banned APIs
|
|
413
|
+
(one table: Shader.WarmupAllShaders on URP, UnityEngine.Input on the Input System
|
|
414
|
+
only, -quit with -runTests…); a Shader.Find nothing references; GLBs glTFast cannot
|
|
415
|
+
decode (meshopt, Basis, WebP); ProjectSettings drifted from the project's own setup
|
|
416
|
+
method; the fixes past builds repeated (48 kHz, Unity audio switched off,
|
|
417
|
+
mipmapped NPOT and uncompressed textures with their MB, .meta files an import rule
|
|
418
|
+
rewrites, a productGUID, metroPackageName or .gitattributes cloned from another project
|
|
419
|
+
on this Mac, which branch and commit each file: shared package compiles, Firebase on the
|
|
420
|
+
Simulator, ungated launch flags, EditorApplication.Exit outside batch mode, the
|
|
421
|
+
netstandard guard, IL2CPP numbers, Simulator UDIDs typed into scripts), each a warn or
|
|
422
|
+
a note with its fix but a build that plays no sound; then this Mac: the Editor and the
|
|
423
|
+
build modules each target needs, with the
|
|
424
|
+
Unity Hub line, and for iOS the distribution certificate, Xcode account and App Store
|
|
425
|
+
Connect key, each present or missing (names only, never a secret);
|
|
426
|
+
--add-hooks writes src/fpx-hooks.ts into a game that has no __FPX_* hooks;
|
|
427
|
+
--host reads the host agent's own permission settings (Claude Code, Codex, Cursor
|
|
428
|
+
CLI, Kiro CLI; detected when no name is given): which commands the kit runs it lets
|
|
429
|
+
through, which shapes (chains, heredocs, loops, python) it may refuse and a plain
|
|
430
|
+
call for each, and the allowlist lines you could add. Reads, never writes; exit 0
|
|
431
|
+
upgrade update the files funtools wrote; never overwrites your changes. --discard: say no to
|
|
432
|
+
the waiting <file>.funtools-new copies (deleted, and not offered again until the kit
|
|
433
|
+
changes that file again). --self: the
|
|
434
|
+
newest funtools on npm and the command that installs it. Once a day funtools
|
|
435
|
+
looks for one in the background and says so after a command (never in CI, with
|
|
436
|
+
--json or off a terminal; FUNTOOLS_NO_UPDATE_CHECK=1 turns it off)
|
|
437
|
+
smoke static checks of what ships (exit 1 = a player will see it)
|
|
438
|
+
smoke-build the 10-minute build before any full build: init --bare, the plan's locked sheets pinned, one
|
|
439
|
+
picture and one 3D model through the hosted tools, placed, captured, the world read, the target
|
|
440
|
+
scored, one card. Pass/fail on a model in the world, the locked look, no primitives, a real card.
|
|
441
|
+
Needs a key and Chromium; costs one picture and one model (.funtools/smoke-build.json)
|
|
442
|
+
test run the game's npm test and record its exit with the build fingerprint
|
|
443
|
+
(.funtools/tests.json); exit 0 pass · 1 fail · 3 no test script. --node runs
|
|
444
|
+
test/**/*.test.ts with this Node directly (no npm, nothing on PATH; imports without
|
|
445
|
+
an extension resolve to .ts), and an npm test that cannot start (exit 126/127) falls
|
|
446
|
+
back to it on its own
|
|
447
|
+
--goals: one bounded player per goal the GDD's behaviour tests state (test/goals/<id>.json,
|
|
448
|
+
written for each goal sentence with none: observe = how met is seen in __FPX_STATE__,
|
|
449
|
+
route = journey steps, caps = steps/seconds/loop): the route replayed in a real browser
|
|
450
|
+
and a seat prompt for a fresh agent (.funtools/goals/<id>/seat.md, its answer read back);
|
|
451
|
+
each goal met · not met · blocked (could not act, or stuck) · not observed, in
|
|
452
|
+
.funtools/goals.json. Never changes the exit code
|
|
453
|
+
check build, the rules suite and verify in one call (the loop's chain, for hosts that
|
|
454
|
+
refuse \`&&\`): a red build stops there; exit 3 a step could not run · 1 one failed ·
|
|
455
|
+
2 verify observed nothing · 0 all passed. Then notes and warnings that never change
|
|
456
|
+
the exit: the production mirror; the frame contract (needs → systems: a starter part
|
|
457
|
+
you replaced is measured for the basic it carried, e.g. the safe HUD band, a save that
|
|
458
|
+
survives a reload); the look (src/look.json: tokens the game would drop, contrast with
|
|
459
|
+
inks proposed, the page's "[frame] look fallback" lines); the idle run (no input after
|
|
460
|
+
Play for 20 s: time alive, deaths, how it ended, warned under the genre's floor;
|
|
461
|
+
.funtools/idle.json; --no-idle skips it). --phone also verifies on a phone and
|
|
462
|
+
measures the band there (pieces under the notch or home bar, HUD cover of the play).
|
|
463
|
+
The world, for a 3D game or one with __FPX_ACTORS__ (--world forces, --no-world
|
|
464
|
+
skips): boxes (flat boxes near the camera, grounding) and life (animated vs a picture
|
|
465
|
+
slid or bobbed, travel, crowd, flyers), read as the slice gate and its card read them.
|
|
466
|
+
Each step lands in .funtools/check.json as it finishes: a run stopped partway (a
|
|
467
|
+
signal, or killed under load with exit 137) keeps what it measured and says it was
|
|
468
|
+
cut short (at once, or at the next check)
|
|
469
|
+
sim the rules played by bots over seeds (the game's test/probes.ts on src/probe-kit.ts,
|
|
470
|
+
engine-free, free): standing still wins, no skill ceiling (a naive steer-to-target
|
|
471
|
+
player does as well as a sharp one), the taught line loses, the novice floor, a
|
|
472
|
+
never-happens check, play length (a skilled run under 3 min on one screen, 5 with
|
|
473
|
+
levels, or under the design doc's play-length line), a sharp bot that stalls (bot
|
|
474
|
+
broken), the best staged moment, music that follows play. Spread and per-level
|
|
475
|
+
splits, the probe's stderr printed; --timeout stops the probe (default 300 s). A
|
|
476
|
+
mirror: warnings never fail it; records .funtools/sim.json
|
|
477
|
+
exit 0 measured · 2 nothing measured (no probe script) · 3 error
|
|
478
|
+
sim lease|release|list|gc|shot|tap: an iOS Simulator of the agent's own. \`lease\`
|
|
479
|
+
creates a fresh one named ft-lease-<id>, records its owner (the agent's process:
|
|
480
|
+
--owner, FUNTOOLS_OWNER_PID or the host's), boots it headless and prints its udid;
|
|
481
|
+
\`release <udid>\` shuts down and deletes that lease and only a lease (another live
|
|
482
|
+
owner's needs --force); \`list\` shows leases, owners and states; \`gc\` deletes leases
|
|
483
|
+
whose owner is gone (--dry-run lists them), and only ever ft-lease-* devices. \`shot\`
|
|
484
|
+
saves the screen upright in the game's orientation (--orientation, else the app's
|
|
485
|
+
Info.plist: --app or the one app running, else what the lease last used); \`tap\`
|
|
486
|
+
presses (x, y) of that upright picture (pixels; --points), mapped onto the portrait
|
|
487
|
+
digitizer, through baguette or else idb (--tool; simctl cannot tap; on the home
|
|
488
|
+
screen a baguette tap reads as a long press), on a lease only. Never
|
|
489
|
+
quits Simulator.app, never \`simctl shutdown all\`, never touches a device it did not
|
|
490
|
+
create. Records in ~/Library/Caches/funtools/sim-leases
|
|
491
|
+
exit 0 done · 2 not observed (no tap tool) · 3 refused or error
|
|
492
|
+
unity Unity on a Mac several agents share. \`unity run -- <unity args>\`: one batch job through
|
|
493
|
+
the machine-wide queue — one job per Unity project path across every session and
|
|
494
|
+
repo, at most --max Unity batch runs on the Mac (default 2, FUNTOOLS_UNITY_MAX;
|
|
495
|
+
Unity runs no funtools job owns count too). While it waits it prints who holds the
|
|
496
|
+
lock (session, pid, command, since). Liveness is progress: the holder's log grows or
|
|
497
|
+
its processes use CPU; a holder that is gone or makes no progress for
|
|
498
|
+
--stale-minutes (default 20, FUNTOOLS_UNITY_STALE_MINUTES) is reclaimed with a
|
|
499
|
+
message. An Editor with the project open refuses the run (it is never quit for you).
|
|
500
|
+
Adds -batchmode, -quit (not with -runTests or --no-quit), -projectPath (the cwd or
|
|
501
|
+
--project) and a per-job -logFile (Logs/funtools-unity-<when>-<id>.log, or --log);
|
|
502
|
+
the Editor is the project's ProjectVersion.txt one from Unity Hub (--unity,
|
|
503
|
+
UNITY_PATH). Prints the log's errors and passes Unity's exit code through.
|
|
504
|
+
\`unity jobs\`: holders, waiters, slots and outside Unity runs. \`unity gc [roots…]\`:
|
|
505
|
+
Library/, Builds/, Temp/ and Logs/ untouched for --days (default 14) under the roots
|
|
506
|
+
(default the cwd), with sizes; a dry run unless --yes; a project Unity has open, whose
|
|
507
|
+
lock is held or a job waits for, is skipped. Locks in ~/Library/Caches/funtools/unity-locks.
|
|
508
|
+
\`unity gc --disk\`: the whole disk, advice first. Free space against --below GB (default 20),
|
|
509
|
+
then what is safe to reclaim, largest first, each with its size, why and the one command:
|
|
510
|
+
this repo's worktrees already in the current branch, clean, with nobody in them (git
|
|
511
|
+
worktree remove, never --force); cargo target/ (sized by listings and stat, never du;
|
|
512
|
+
reclaim with scripts/target-gc.sh; not measured while cargo runs); Xcode DerivedData and
|
|
513
|
+
agents' *-dd temp folders untouched --days (default 2) with no xcodebuild on them; idle
|
|
514
|
+
Unity folders; .funtools/unity scratch older than --days. ~/Library/Caches, the Trash and
|
|
515
|
+
Docker are sized, never touched. Walks are bounded (≥ means at least) and never follow a
|
|
516
|
+
symlink. A report; --apply deletes only the kinds named with --only (worktrees,
|
|
517
|
+
derived-data, unity, scratch), each re-checked first. Exit 0 above the threshold, 1 below
|
|
518
|
+
(advice; --strict marks it FAIL), 125 refused
|
|
519
|
+
exit Unity's own code · 125 nothing ran (refused, gave up waiting)
|
|
520
|
+
\`unity probe\`: the real player measured (com.funpixel.probe in a development build; add it
|
|
521
|
+
with \`unity probe install\`, which prints the one manifest line). Builds the player through the
|
|
522
|
+
queue or reuses a current one (Builds/FunpixelProbe/), launches it — a Mac player hidden and
|
|
523
|
+
muted (--visible, --sound), a Simulator on a lease of its own (--udid one you leased) — plays
|
|
524
|
+
--path's steps (taps, drags, keys, waits, scenes, segments, shots, expects), measures --seconds,
|
|
525
|
+
and reads frame/main/render/work time p50 p95 p99 max, hitches by frame, draws, SetPass,
|
|
526
|
+
triangles, GC per frame and memory, warm-up/capture/load frames left out. Judged against the
|
|
527
|
+
tier's budgets (--tier phone|tablet|mac, --budget); a hitch whose own work stayed small is
|
|
528
|
+
flagged as the host's. Writes .funtools/unity/probe.json. \`unity play\`: a remote-play
|
|
529
|
+
session (start, then tap/drag/key/pad/state/ui/shot/record, end), in the game's own upright
|
|
530
|
+
pixels (--points). \`unity capture --all\`: one still of each registered view, else each scene,
|
|
531
|
+
from a capture build with no development watermark (--dev: the development player)
|
|
532
|
+
exit probe 0 pass · 1 over budget or a step failed · 2 not observed · 3 error
|
|
533
|
+
\`unity bridge status|install|remove\`: MCP for Unity (CoplayDev/unity-mcp, MIT) in a
|
|
534
|
+
project, pinned to v10.3.0. status reads the manifest, the Editor on the project, whether
|
|
535
|
+
its bridge answers, the queue and uv. install adds com.coplaydev.unity-mcp#v10.3.0 to
|
|
536
|
+
Packages/manifest.json — one line, nothing else, printed — and remove takes it out; both
|
|
537
|
+
refuse while the queue holds the project or a Unity has it open.
|
|
538
|
+
\`unity editor <action>\`: console [--since n|last] [--count 50], compile, play, stop, shot
|
|
539
|
+
[--game|--scene] [--out file], menu "<path>", call <Namespace.Class.Method>, hierarchy
|
|
540
|
+
[--depth 3], add <cube|sphere|capsule|cylinder|plane|quad> [--name] [--at x,y,z], save,
|
|
541
|
+
open, close [--force]. With an Editor on the project whose bridge answers, it goes through
|
|
542
|
+
the pinned server over stdio (uvx), routed by set_active_instance to that Editor's exact
|
|
543
|
+
instance id and checked by its project root before any call — never another Editor. With
|
|
544
|
+
none: compile, call and menu (a [MenuItem] in the project's own scripts) run as a queued
|
|
545
|
+
batch job, console reads the newest funtools log, each said as a fallback; play, stop,
|
|
546
|
+
shot, hierarchy, add and save need an Editor: --open opens one with the bridge on stdio
|
|
547
|
+
for that session (through the queue; pid recorded), --wait seconds for its bridge (300).
|
|
548
|
+
close ends only an Editor funtools opened (its recorded pid, checked as that Editor).
|
|
549
|
+
An Editor someone else opened is never quit, relaunched or closed.
|
|
550
|
+
exit (editor) 0 done · 1 failed · 2 not observed (needs an Editor) · 3 refused or error
|
|
551
|
+
spec the big tier of the plan, as a folder the game loads (design/spec/). \`spec init\` (a game
|
|
552
|
+
past the small tier, or --tier mid|deep; a Unity game gets the folder alone, shaped for
|
|
553
|
+
its Unity project to load, and never the web files) writes the README build contract,
|
|
554
|
+
tunables.json with every named number the code holds (src/tune.ts loads it:
|
|
555
|
+
tune('group.name'), typed, a missing id throws, hot in dev), rules, rooms and chapters
|
|
556
|
+
as data (rooms.json, two
|
|
557
|
+
chapters of three rooms of the starter's round), screens and tutorial, difficulty, and at
|
|
558
|
+
deep the rooms in words, cast, story, audio and juice; scaffold files still as written
|
|
559
|
+
are rewired to read the numbers. \`spec lint\` holds it against itself and the code:
|
|
560
|
+
sections the tier needs, unknown and unused ids, a tunable's number typed into src/,
|
|
561
|
+
rooms without a par or an exit, the pars against the play floor, a room changed before
|
|
562
|
+
it was opened; records .funtools/spec.json (the GDD stage's gate past the small tier)
|
|
563
|
+
exit 0 written / clean · 1 lint warnings · 2 nothing to do · 3 error
|
|
564
|
+
rooms every room of design/spec/rooms.json played in order by bots over 5 seeds (test/rooms.ts
|
|
565
|
+
on src/probe-rooms.ts; a lost round retries from the room's door): a careful player at
|
|
566
|
+
a human rate clears each on every seed within 50% of its par, the sharp bot too, the
|
|
567
|
+
naive and novice players' falls room by room, the pars against the play floor; records
|
|
568
|
+
.funtools/rooms.json (the expand stage's gate). \`rooms open <room>\` is slice first: it
|
|
569
|
+
opens a room for building only once every room before it is open and the one before
|
|
570
|
+
passes (the slice's core test and slice stage too); the owner alone releases one early
|
|
571
|
+
(--by owner, their words kept). \`rooms plan\` records the rooms as planned now (a room
|
|
572
|
+
changed before it was opened was built early): free until the owner agrees the plan
|
|
573
|
+
exit 0 every room passes / opened · 1 a room fails / refused · 2 nothing to play · 3 error
|
|
574
|
+
depth the experience evidence stills cannot give, free: the content inventory (what the
|
|
575
|
+
design declares, what the game ships, what real play reached), the pacing timeline of
|
|
576
|
+
the longest real sessions (blind rounds and journeys on game time: beats, places and
|
|
577
|
+
firsts a minute, the longest flat stretch, tension peaks and releases when the state
|
|
578
|
+
names one), the blind player's come-back answer, the design's experience dimensions,
|
|
579
|
+
the bots' skill spread. Facts X1… for the experience seats, text.md (every sentence a
|
|
580
|
+
player can read), a beat strip; in .funtools/depth/ (--out elsewhere). The story's
|
|
581
|
+
## Story beats against the beats real play showed (__FPX_STATE__.beat; "Story:
|
|
582
|
+
declined" is a choice) and each content-list row against what play reached; --fill
|
|
583
|
+
writes them into the design document's Reached in play / Seen in play cells. A mirror
|
|
584
|
+
exit 0 measured · 3 error
|
|
585
|
+
reach can the player get there, free and local, no browser: each level kept as data (a tile
|
|
586
|
+
grid, side or top view, or the build_level 3D spec; funpixel.json "reach" names them,
|
|
587
|
+
else design/level.json and design/levels/) is filled from its spawn, and every exit,
|
|
588
|
+
pickup, checkpoint or room nothing reaches is named with the gap in the way: wider or
|
|
589
|
+
higher than the game's own jump (reach.movement: numbers or rule constants), or a wall
|
|
590
|
+
with no way through. Sealed pockets are warned. Records .funtools/reach.json
|
|
591
|
+
exit 0 all reached · 1 one cannot be · 2 not observed (no level data, no spawn, a side
|
|
592
|
+
view with no movement declared) · 3 error
|
|
593
|
+
world a seeded world as data the game loads, free and local: \`forge\` writes
|
|
594
|
+
design/world/world.json — chapters of rooms joined by doors, a critical path from the
|
|
595
|
+
arrival room to the goal room, side rooms, loops and a secret; each room a top-down tile
|
|
596
|
+
grid (2D: wall, floor, hazard, arrival, goal, pickup, door) or a heightfield in its own
|
|
597
|
+
file plus placed props (3D), named places (arrival, goal, reward, vista, landmark), a light
|
|
598
|
+
and fog of its own over the world's, and a budget estimate. Props are the game's own kinds
|
|
599
|
+
(GDD **Kit:** line, or "props" in a JSON brief) or neutral roles (cover, landmark, light,
|
|
600
|
+
dressing, marker); chapter names and landmarks come from ## Places. The same seed and
|
|
601
|
+
brief give the same bytes; the grammar itself is drawn per seed. A file changed by hand is
|
|
602
|
+
kept unless --force. --variety N forges N seeds in memory and scores how far apart they
|
|
603
|
+
are (shapes, layouts, grammar, light, graph), naming near-twins. \`check\` validates it:
|
|
604
|
+
ids, every door on both sides, each room's doors, goal and places reached from its
|
|
605
|
+
arrival (3D: a rise of at most movement.step between cells, solid props closed), every
|
|
606
|
+
room reached through the graph, budgets. The loader: src/world.ts
|
|
607
|
+
exit forge 0 written · 3 error; check 0 pass (warnings too) · 1 fail · 2 no world.json · 3 error
|
|
608
|
+
feel the verbs, tells, deaths and poses as a player meets them: a script plays the real
|
|
609
|
+
controls on the built game (test/feel.json: journey steps plus \`until field=value\` and
|
|
610
|
+
\`retry <step>\`; or --journey <name>) while every frame's __FPX_STATE__ is traced. Reads
|
|
611
|
+
the design's numbers (design/GDD.md "## The numbers", the script's "expect") back with
|
|
612
|
+
the gap: jump height, time in the air, apex hang, top speed and time to it, dash
|
|
613
|
+
distance and time, invulnerable window, cooldown; tell to strike and the defensive
|
|
614
|
+
verb's safe window; the death beat and death to control again; each actor's look in
|
|
615
|
+
each of its states. With "sweeps" in the script, coyote time and the jump buffer from
|
|
616
|
+
timed attempts across runs (a fresh load each; --no-sweep skips). Times are on the
|
|
617
|
+
game's clock when __FPX_STATE__ gives t (seconds), else the page's. A mirror: notes
|
|
618
|
+
never fail it; records .funtools/feel.json
|
|
619
|
+
exit 0 measured · 2 nothing measured (no script, no state) · 3 error
|
|
620
|
+
gdd the GDD lint and design reads for design/GDD.md (else design/README.md), no score
|
|
621
|
+
(completeness is not quality): the invention's six answers (answered or generic; an
|
|
622
|
+
answer is its label's line and the sub-bullets under it), hits per axis with sources,
|
|
623
|
+
named behaviour tests (and how many the test files hold), the studio pitch's locked core, then the sections missing or thin; records
|
|
624
|
+
.funtools/gdd.json with the document's sha256 and lists the live decisions of
|
|
625
|
+
design/decisions.md; \`gdd page\` writes design/GDD.html (the document as a page to read and rate; the
|
|
626
|
+
open questions as owner-decision rows under the board) and design/first-look/index.html.
|
|
627
|
+
Then the design measures, advice with a number and why each, "not given" when the GDD has no
|
|
628
|
+
field for one: elements per hour of first playthrough (warns above about 4; peers 1.4–3), the
|
|
629
|
+
spine (a system serving no clause), sources (a marked claim with no source line; --sources
|
|
630
|
+
lists every one), peers (length and elements per hour, else unknown), length totals (the
|
|
631
|
+
parts' minutes against the stated length) and the Steam two-hour check (the hook inside the
|
|
632
|
+
refund window). --strict makes a measure's warn exit 1 (a CI's choice)
|
|
633
|
+
exit 0 read · 1 a measure warns under --strict · 2 no document (or no first-look folder) · 3 error
|
|
634
|
+
greenlight the plan's lock and its worth read: \`lock --version v1.0\` pins the design, the bible and
|
|
635
|
+
the target frames (\`flow agree --by owner\` runs it); \`page\` writes design/greenlight.html (pitches, bible, decisions, lock); \`worth\`
|
|
636
|
+
sets the plan's own minutes against the owner's target and the genre's best, one verdict, a
|
|
637
|
+
scope question to the owner when it does not fit; .funtools/worth.json
|
|
638
|
+
exit 0 locked or met · 1 held · 2 not observed · 3 error
|
|
639
|
+
verify run the game probe locally and write verify.json
|
|
640
|
+
exit 0 pass · 1 fail · 2 not observed · 3 error
|
|
641
|
+
--front-door presses the title's real start control ([data-fpx-start], or
|
|
642
|
+
--click's selector) instead of entering play through the hooks; a missing or
|
|
643
|
+
dead button fails
|
|
644
|
+
--players 2: two tabs in one room on a local relay — the host acts and the
|
|
645
|
+
guest must see it, a late joiner gets the state, the room survives the host
|
|
646
|
+
leaving (needs the funpixel-relay binary and a Redis)
|
|
647
|
+
--engine webkit (also capture, play, journey, feel) runs Playwright's WebKit,
|
|
648
|
+
Safari's engine without Safari's UI, for a bug only Safari shows; recorded in
|
|
649
|
+
.funtools/verify/webkit.json, never verify.json (publish reads Chromium's).
|
|
650
|
+
Optional: npx playwright install webkit (doctor says so)
|
|
651
|
+
capture one PNG of a named view: entered through __FPX_VIEWS__, --verbs pressed in
|
|
652
|
+
order, held --hold ms (600) and frozen for the shot (default
|
|
653
|
+
.funtools/capture/<view>-<viewport>.png); no --view shoots the page as loaded.
|
|
654
|
+
A signature moment (a view declared moment: true) plays instead: a burst of
|
|
655
|
+
--burst frames (12, ~1.8 s) into <out>.burst/, and the frame showing its
|
|
656
|
+
__FPX_MOMENT__ mark is the shot. A phone is touch, with the Studio's touch
|
|
657
|
+
emulation (?funpixel_touch=1); --safari shrinks it to the page Mobile Safari's bars
|
|
658
|
+
leave (390x684 upright, 724x283 sideways; <view>-phone-safari.png), where a
|
|
659
|
+
height-sized play is narrower than the 390x844 frame shows. --all shoots every declared view at both sizes,
|
|
660
|
+
each to its default path: the set the critic panel and the GDK's Stop gate read.
|
|
661
|
+
Repeatable (8 Oct): the page's clock is held from before the game's first line and
|
|
662
|
+
moved in fixed 1/60 s frames from seed 1 (--seed n; window.__FPX_SEED__, Math.random),
|
|
663
|
+
with no game time passing while the page waits on the network, a decode or a resize,
|
|
664
|
+
so two captures of a view are the same frame. Each shot says how it was framed (held
|
|
665
|
+
clock, seed, frames, the named camera from __FPX_CAMERA__) or that it ran free
|
|
666
|
+
(--free-running: the wall clock, as before), in --json and .funtools/capture/index.json
|
|
667
|
+
exit 0 shot · 2 the view was not entered · 3 error
|
|
668
|
+
compare before and after on the same views: --baseline keeps the current captures (capture
|
|
669
|
+
--all) as the before; after the change and a fresh capture --all, compare writes
|
|
670
|
+
each view's before | after sheet to .funtools/compare/ and names the views that
|
|
671
|
+
did not change, and each pair not framed alike (free-running, another seed, game time
|
|
672
|
+
or camera) as not comparable frame for frame; exit 0 compared · 2 nothing to compare · 3 error
|
|
673
|
+
--target: the game today beside the frame it aims at, at fixed framings (a phone held
|
|
674
|
+
sideways 852x393, desktop 1280x720; --viewport, default both), one strip per view and
|
|
675
|
+
size, "today | target", to design/look/<view>-<viewport>.png (--out: a folder, or one
|
|
676
|
+
file for one strip). One picture serves every --view; or one picture per view, in
|
|
677
|
+
order. The target is shown whole (--fit cover fills and crops). Read the strip for
|
|
678
|
+
mood, light, the hero's size and life. Today's frame is held like capture's (--seed,
|
|
679
|
+
--free-running); exit 0 made · 2 a view not entered · 3 error
|
|
680
|
+
parts cut a generated sheet into pieces: a character into parts or poses, an item sheet
|
|
681
|
+
into items. Background keyed (--key auto: the sheet's alpha, else its flat corner
|
|
682
|
+
colour flooded in from the edges), each piece trimmed with --pad px, written as a
|
|
683
|
+
transparent <name>.png (--webp adds lossless WebP) plus parts.json (box on the sheet,
|
|
684
|
+
size, pivot: bottom-centre for poses, --pivot top for limbs). Pieces: --grid CxR, a
|
|
685
|
+
--boxes file ([{name, x, y, w, h}]), or every separate figure (bits within --merge px
|
|
686
|
+
join); --names in reading order. Default out: <sheet>-parts/ beside the sheet.
|
|
687
|
+
Warns on a background that will not key, a piece cut by the edge, names that do
|
|
688
|
+
not match; exit 0 written · 2 nothing found · 3 error. --key paper keys a painted
|
|
689
|
+
sheet's paper (colour and grain from the whole border, patches between legs too);
|
|
690
|
+
--clean runs cut's clean pass on every piece
|
|
691
|
+
cut parts with a painted sheet's defaults: figures off a cream or plain reference sheet
|
|
692
|
+
(or a generated --transparent image) as clean sprites, local and free. The paper is
|
|
693
|
+
keyed; then per piece: painted ground shadows under the feet go (--keep-shadows keeps
|
|
694
|
+
them), the soft halo goes (alpha thresholded, a 1 px soft rim), the rim takes its
|
|
695
|
+
colour from the inside (no paper bleed), stray islands go, and lines ruled across the
|
|
696
|
+
sheet (a generator's cell grid) go first. Each piece says what was removed; parts.json
|
|
697
|
+
carries the counts. Default out: <sheet>-cut/ beside the sheet. Exit as parts
|
|
698
|
+
audit-screens layout on every declared view (or --views): text under 12 px, hit areas
|
|
699
|
+
under 44 px, HUD pieces over each other, anything cut by the screen edge or
|
|
700
|
+
inside a phone's safe area; developer copy on screen ("will be here", TODO,
|
|
701
|
+
lorem, NaN) is a warning beside the verdict
|
|
702
|
+
exit 0 pass · 1 fail · 2 not observed · 3 error
|
|
703
|
+
A phone is held the way funpixel.json's orientation says; --orientation turns it
|
|
704
|
+
(the other way a player can hold it; export audits both) for capture,
|
|
705
|
+
audit-screens, scene and play: a portrait game held sideways is 844x390,
|
|
706
|
+
shot to <view>-phone-landscape.png, never counted as the game's phone shot
|
|
707
|
+
scene the engine's scene graph (Three, Pixi, Phaser) read in the page: nodes, depth,
|
|
708
|
+
what is switched off or outside the view, textures and a draw-call estimate
|
|
709
|
+
exit 0 read · 2 no scene graph found · 3 error
|
|
710
|
+
--motion samples ~1 s of play: each actor (funpixel.json "actors" player, threats,
|
|
711
|
+
pickups, else the usual names), judged against the Motion column of the design
|
|
712
|
+
note's ## Actors table (moves · animates · still); recorded in .funtools/scene/motion.json
|
|
713
|
+
exit 0 every declared mover moved · 1 one stood still or never changed · 2 not observed
|
|
714
|
+
hook the hook moment: the title, the real Play, then the first minutes of play (--seconds,
|
|
715
|
+
default 180) read at checkpoints: what moves besides the player, how much of each frame
|
|
716
|
+
is flat colour (empty), whether the picture changes, stand-ins the game still names
|
|
717
|
+
(__FPX_STATE__().art.placeholders). A light player taps the middle every 3 s (cards
|
|
718
|
+
between Play and the world are passed); --verbs presses verbs in turn instead, --idle none.
|
|
719
|
+
Frames in .funtools/hook/, record .funtools/hook.json. Warns, never fails
|
|
720
|
+
exit 0 measured · 2 not observed · 3 error
|
|
721
|
+
life is the world alive around the player? ~3 s of the play view (--view, else the view
|
|
722
|
+
marked motion): the pieces that move or change besides the player (crowds of
|
|
723
|
+
instances, particles, lights), whether a looping sound bed started, and for each big
|
|
724
|
+
moment (views marked moment, or --moment) whether anything answered its __FPX_MOMENT__
|
|
725
|
+
mark (a sting, a crowd, pieces or lights) against play before it. The front door
|
|
726
|
+
([data-fpx-start]) is pressed first, since sound starts on a gesture. Warns, never fails;
|
|
727
|
+
records .funtools/life.json (funpixel-world-and-story, references/atmosphere.md).
|
|
728
|
+
Motion is not animation: people and creatures are judged animated (frames, bones,
|
|
729
|
+
morphs or parts change), SLIDES LIKE A STONE (travel with nothing inside changing),
|
|
730
|
+
RIGID (bob or turn as one picture) or STILL; no idle and reactions near the player
|
|
731
|
+
are counted (@funpixel/verify life_anim.js). Travel is watched ~10 s and labelled so;
|
|
732
|
+
walk.json, when walk ran, decides it. A moment that fires every cue at once (a jump
|
|
733
|
+
through the payoff) is flagged, not counted as an answer
|
|
734
|
+
exit 0 measured · 2 not observed (no scene graph and no sound log) · 3 error
|
|
735
|
+
walk verify the world by playing it: every route in funpixel.json walk.routes (else one
|
|
736
|
+
explore walk) from the TITLE with real keys and clicks, held and steered in real time
|
|
737
|
+
(no view jumps, no verbs), filmed from the play camera (.funtools/walk/<route>.webm,
|
|
738
|
+
MediaRecorder on the canvas). Each tick reads the actors contract (__FPX_ACTORS__,
|
|
739
|
+
HOOKS.md "Actors") and warns on CLIPPING (bodies through props, walls, each other),
|
|
740
|
+
PLACEMENT, FLOATING, SCALE (funpixel.json "scale" in metres; "framing" share of the
|
|
741
|
+
frame), STALLS (presses that change nothing: blocked or a dead end), NO CHALLENGE (no
|
|
742
|
+
stumble, detection, timer or failure fired) and life (slides like a stone, no idle,
|
|
743
|
+
no reaction; a crowd that never travels; the hero's face on a crowd; flyers without a
|
|
744
|
+
flap), LOSS (a run that ends failed, then the game's retry; penalties alone are not a
|
|
745
|
+
loss). A route's "then" (or --chain a,b) walks legs in one page and one film from the
|
|
746
|
+
title (a prologue, then Run 01; a chained leg skips its start steps and plays its
|
|
747
|
+
"continue" steps); framing warns when over 15% of the time is outside the band;
|
|
748
|
+
steer.waypoints (or a path in the state) steers by position when there is no
|
|
749
|
+
lateral field; a leg that arrives in under 3 s is not observed. CROWD: clumps
|
|
750
|
+
(people within 1.5 m with no shared scene: tagged, or inferred when they face each
|
|
751
|
+
other or move together), empty stretches of the path, the count against
|
|
752
|
+
walk.crowd.people. Every 4 s the world itself: BOXES (big meshes that read unfinished,
|
|
753
|
+
the worst frame kept as <route>-boxes.png; BOX FIGURES) and OFF THE GROUND (big props
|
|
754
|
+
and vehicles hovering, over a drop or sunk), as funtools boxes reads one view. The
|
|
755
|
+
contract: skill funpixel-verify,
|
|
756
|
+
references/actors.md. Records .funtools/walk.json. Warns, never fails
|
|
757
|
+
exit 0 walked · 2 not observed (no route reached play, or nothing to read) · 3 error
|
|
758
|
+
score how close a moment is to its target picture, 0-100 on six axes (composition, hero,
|
|
759
|
+
identity, palette and light, density, life) at compare --target's framing: measured
|
|
760
|
+
numbers (palette ΔE, layout and edge similarity, detail share, the hero's height share
|
|
761
|
+
from the actors contract against the target's hero_share, motion over 3 s) plus a blind
|
|
762
|
+
vision judge when OPENROUTER_API_KEY is set (else "not observed"). A loading or splash
|
|
763
|
+
frame is never counted. Every moment in funpixel.json "moments" without --target; the bar
|
|
764
|
+
is "accuracy_min" (default 75, the owner's; "accuracy_stretch" 90 kept). Every moment
|
|
765
|
+
also gets its FIX LIST: the camera the game runs (read in the page: __FPX_CAMERA__, the
|
|
766
|
+
three.js camera or the pixi world) and the numbers that would lay the capture over the
|
|
767
|
+
target ("SHOTS.play.height 4.4 → 6.1, fov 50 → 41"), the light (exposure ×k), colours
|
|
768
|
+
by hex and bare regions to dress. Writes design/look/scores.json and one line per run
|
|
769
|
+
to design/look/score-history.jsonl; \`--fixes\` prints the last run's list without a
|
|
770
|
+
browser. The flow's rounds work down it. The moment is shot on capture's held clock
|
|
771
|
+
(--seed, --free-running), so a round's score before and after a change reads one frame
|
|
772
|
+
and says how it was framed. Warns below the bar. Beside the axes, EYE COMFORT (never in
|
|
773
|
+
the score): each frame's busyness (the mean brightness step between neighbouring pixels,
|
|
774
|
+
on a grey copy 360 px on its short side) and clipped share (luma <= 2 or >= 253) against
|
|
775
|
+
its targets'; a frame over 14, over 1.25x its targets' busiest, or over 6% clipped warns
|
|
776
|
+
(match the targets' quality, not their density) and is stamped on zero's comfort counter
|
|
777
|
+
exit 0 scored · 2 nothing scored · 3 error
|
|
778
|
+
framing where each actor lands on screen at the target framing: its box, its height share
|
|
779
|
+
and whether it is in view, against funpixel.json "framing" ({ "ren": [0.16, 0.22] }),
|
|
780
|
+
so a camera is tuned without rebuild-and-shoot rounds
|
|
781
|
+
exit 0 read · 2 none of the actors found · 3 error
|
|
782
|
+
boxes is the world made, or boxes? Each play view (or --view a,b) is held and its frame
|
|
783
|
+
rastered from the play camera beside a screenshot (@funpixel/verify surfaces.js): big
|
|
784
|
+
meshes that read as unfinished (flat colour on big triangles, few vertices for their
|
|
785
|
+
size, no texture) are named, ranked by area and nearness, beside the most detailed
|
|
786
|
+
structure in the frame (terrain and rock in a faceted style lock are the look, not
|
|
787
|
+
boxes); people and creatures built of a handful of boxes are named at any size (BOX
|
|
788
|
+
FIGURES); the big props and vehicles are read against the nearest surface under where
|
|
789
|
+
they touch down, terrain or a support (a deck, a pad, what people stand on; grounding.js:
|
|
790
|
+
hovering, over a drop, sunk). walk does both along its
|
|
791
|
+
routes. The dressing round the player is counted per 100 m² against the place's
|
|
792
|
+
target (state.world.dressing.target), the road kept clear, the phone's frame budget
|
|
793
|
+
(DRESSING: BARE, ROAD CLUTTERED, OVER THE PHONE BUDGET; dressing.js).
|
|
794
|
+
Writes .funtools/boxes.json and .funtools/boxes/<view>.png. Warns, never fails
|
|
795
|
+
exit 0 read · 2 not observed (no Three scene) · 3 error
|
|
796
|
+
places are the places distinct in play (the Silt bar's S8: ≥ 3)? Each place-<id> view (or
|
|
797
|
+
--view a,b) is entered and shot; each pair compared in colour (ΔE over a 4 × 4 grid)
|
|
798
|
+
and layout (variety's limits) and in the light the game says it runs there
|
|
799
|
+
(state.world.light: preset, exposure, key, fill). Alike in colour and light = one
|
|
800
|
+
place repainted. Writes .funtools/places.json and .funtools/places/<view>.png. Warns
|
|
801
|
+
exit 0 read · 2 not observed (under two place views) · 3 error
|
|
802
|
+
compare-takes two takes of one game (two parallel builds of the same plan), each played from
|
|
803
|
+
the title with walk (its routes, filmed) and scored against its targets with score
|
|
804
|
+
when funpixel.json names moments; a best-of-both table per area (environment,
|
|
805
|
+
dynamics, challenge, accuracy, each moment: which take is ahead and what the mix
|
|
806
|
+
keeps) and a contact sheet (walk frames, frames from the films with ffmpeg, each
|
|
807
|
+
moment beside its target). Writes <out>/takes.json, takes.md, sheet.png (default
|
|
808
|
+
.funtools/takes). A mirror for choosing, never a verdict
|
|
809
|
+
exit 0 compared · 2 nothing measured · 3 error
|
|
810
|
+
profile the numbers behind a frame, per view (default: the gameplay views): frame time
|
|
811
|
+
p50/p95/p99/max and long frames after a warm-up, draw calls and triangles counted at
|
|
812
|
+
the WebGL calls, the scene by kind (Three meshes/instances/lights/shadows, Pixi
|
|
813
|
+
sprites/graphics/text/particles/filters), texture memory estimated, JS heap, what
|
|
814
|
+
the page fetched, dist/ size by kind. Saved to .funtools/profile/<view>-<viewport>.json;
|
|
815
|
+
the next run of the same view and viewport prints the change (--against another
|
|
816
|
+
file). --seed sets window.__FPX_SEED__ before the game runs. --software renders on
|
|
817
|
+
the CPU (SwiftShader), where fill costs a desktop GPU hides behind vsync show as frame
|
|
818
|
+
time; --throttle N slows the page's CPU N times, a stand-in for a phone's JS speed.
|
|
819
|
+
--repeat N samples each view N times and reports the median and the p95 range (a busy
|
|
820
|
+
machine moves single runs a lot). Reports, never judges: neither is a phone, so
|
|
821
|
+
compare runs made the same way. --compare reads the run the way the Studio's profiler
|
|
822
|
+
reads a build against the last one (its percentiles, frame-time buckets, burn rate
|
|
823
|
+
and neutral band): every frame kept, frames over the budget (--budget, default the
|
|
824
|
+
profiler's 18 ms) as a burn rate against a 1% allowance, GPU time a frame when the
|
|
825
|
+
browser offers the timer query (else said, never guessed), stored per view in
|
|
826
|
+
.funtools/profile/history/ and compared only with the last run made the same way
|
|
827
|
+
(seed, viewport, throttle, renderer, frames, budget) — "frame p95 16.9 → 20.9 ms (+4.0
|
|
828
|
+
ms, +24%) regression · draw calls 12 → 72 (+60) regression · burn 0.4× → 5.2×"
|
|
829
|
+
exit 0 measured · 2 nothing measured · 3 error
|
|
830
|
+
inspect what is on screen, asked the way a person clicks in the Studio: the platform's
|
|
831
|
+
inspector in the game's own dev server (as \`dev\` mounts it; --url looks at a running
|
|
832
|
+
\`funtools dev\` instead). --at x,y (CSS px, or 50%,80%) names what is there, HUD first,
|
|
833
|
+
then engine objects topmost first; --name finds objects by label or name; --tree prints
|
|
834
|
+
the scene. Each comes with its state (position, scale, alpha, shown or hidden, its box on
|
|
835
|
+
screen, Three userData) and the source file:line that created it, through the source
|
|
836
|
+
maps. Every runtime error so far is listed at its source line, with __FPX_STATE__().
|
|
837
|
+
Never pauses or selects; reports, never judges
|
|
838
|
+
exit 0 answered · 2 not observed (no inspector, no scene, nothing there) · 3 error
|
|
839
|
+
clip clip critics: how the game FEELS, from strips of consecutive frames of play (what
|
|
840
|
+
stills cannot show). Cuts 12-frame 4 × 3 strips from a reel's frames (default
|
|
841
|
+
.funtools/reel/frames: \`reel --frames-only\` first; its payoff marks centre a strip each)
|
|
842
|
+
into .funtools/clip/<n>/ and writes one prompt per seat (player, animator: feel, juice,
|
|
843
|
+
motion, clarity) for your own agent to run; funtools calls no model. \`clip report\`
|
|
844
|
+
reads the answers back. Separate words (panel/clip.md) from the calibrated still panel
|
|
845
|
+
exit 0 ready or reported · 2 no frames or nothing filed · 3 error
|
|
846
|
+
judge two checks on generated art that an aesthetic score cannot make, each one seat for your
|
|
847
|
+
own agent (funtools calls no model; one seat reads the pictures once). structure: fixed
|
|
848
|
+
yes/no questions with evidence (matched pairs, part counts, attachment, lettering, the
|
|
849
|
+
frame); any no fails, never averaged into a score. identity: a model's rendered views
|
|
850
|
+
(.funtools/renders/<name>/ from \`model render\`; nothing is rendered here) or a sprite set
|
|
851
|
+
against the concept it came from (--concept, else the asset manifest's \`from\`), per
|
|
852
|
+
trait (silhouette, palette, features, proportions) with the worst view; major drift
|
|
853
|
+
fails, minor warns. \`judge report\` reads the answer back; an answer about a picture
|
|
854
|
+
that changed since is not counted
|
|
855
|
+
exit (prepare) 0 ready · 2 no picture/concept/views · 3 error;
|
|
856
|
+
(report) 0 pass · 1 fail · 2 not observed (nothing filed, unanswered, stale) · 3 error
|
|
857
|
+
phone the game in real iPhone Safari (WebKit) through baguette's headless iOS Simulator
|
|
858
|
+
(optional: Apple Silicon + Xcode 26 + \`brew install baguette\`; never installed for you):
|
|
859
|
+
boots an iPhone (--sim), opens the built game in Mobile Safari, closes Safari's first-run
|
|
860
|
+
tip, finds the controls through the accessibility tree (sizes; under 44 pt said), taps the
|
|
861
|
+
start control by name, plays --seconds (--taps presses points, --swipes swipes the play in portrait
|
|
862
|
+
and says which __FPX_STATE__ fields each changed), screenshots each step to
|
|
863
|
+
.funtools/phone/<time>/, and reads the page's own frame intervals, errors, __FPX_STATE__
|
|
864
|
+
and safe-area insets (a folder is served with a reporter; a URL is only looked at). Shuts
|
|
865
|
+
the simulator down after (--keep leaves one it booted running). --orientation landscape
|
|
866
|
+
plays too (taps mapped; screenshots turned upright). WebKit's pacing on the Mac,
|
|
867
|
+
not the phone's speed; one simulator at a time
|
|
868
|
+
exit 0 observed · 2 not observed (no baguette, not a Mac, no iPhone simulator) · 3 error
|
|
869
|
+
look --read: can a player tell themselves and the threats from the ground? The play
|
|
870
|
+
view (\`playing\` when declared) frozen, the player and threat boxes read from
|
|
871
|
+
the engine's scene (names matched by funpixel.json "actors", or --player and
|
|
872
|
+
--threats), each measured against the ring of ground around it: ΔE of the
|
|
873
|
+
figure to its ground, the bar the three bar games all pass. With no boxes, the
|
|
874
|
+
style file's role colours (assets/style.json roles hero, harm) or --colours;
|
|
875
|
+
a PNG on disk is read by colour only
|
|
876
|
+
exit 0 pass · 1 an actor merges with its ground · 2 nothing measured · 3 error
|
|
877
|
+
--hud: the HUD while the game plays (default phone): the play view and each
|
|
878
|
+
moment view sampled --samples times, --every ms apart; cover of the play area,
|
|
879
|
+
text size, contrast against the pixels behind each text, HUD over the player
|
|
880
|
+
or a threat, transient pieces piling up; warnings for a worded message over
|
|
881
|
+
the middle of the play, a text gone before it can be read (0.8 s, ~0.3 s a
|
|
882
|
+
word) and developer copy on screen ("will be here", TODO, NaN). The worst
|
|
883
|
+
sample is saved with the HUD outlined (.funtools/look/hud-<view>-<viewport>.png)
|
|
884
|
+
exit 0 nothing found · 1 a finding · 2 nothing measured · 3 error
|
|
885
|
+
--fit: does it fill the windows people give it? 1700x800, 1280x600, 1024x1024 and
|
|
886
|
+
the phone sideways and upright, each opened fresh: how long it stays one flat
|
|
887
|
+
colour (over 3 s is a finding), then the play view's share of the window, flat
|
|
888
|
+
bars at the edges, content bunched in a corner (.funtools/look/fit-<window>.png)
|
|
889
|
+
exit 0 every window filled · 1 a finding · 2 nothing observed · 3 error
|
|
890
|
+
--sheet: the roster at play size before the game exists: every PNG sprite
|
|
891
|
+
(public/assets/sprites, or --sprites) trimmed, at each --sizes height on a
|
|
892
|
+
neutral band and on the ground (--ground, else assets/style.json roles.ground),
|
|
893
|
+
each measured by --read's ΔE; no build needed (.funtools/look/sheet.png).
|
|
894
|
+
An animation sheet (sheet.json names animations) has each cycle measured:
|
|
895
|
+
distinct poses, and a loop's seam (last → first) against its median step
|
|
896
|
+
exit 0 every sprite reads, no cycle pops or freezes · 1 one merges, pops or
|
|
897
|
+
freezes · 2 no sprite · 3 error
|
|
898
|
+
--3d: what a Three.js game's hero objects are made of. The source the entry
|
|
899
|
+
point reaches: files that build the player, rivals and enemies (primitive
|
|
900
|
+
constructors by kind, hand-built buffers, a model loaded) and the models under
|
|
901
|
+
public/. With a build and a browser (not --static), the running play view adds
|
|
902
|
+
each hero node's meshes, triangles and screen share. Code-built heroes pass only
|
|
903
|
+
as a declared style: funpixel.json "art3d" { "heroes": "procedural", "why",
|
|
904
|
+
"proof": "<frame>" }; stand-ins are named in "art3d.stand_ins"
|
|
905
|
+
(.funtools/look/3d.json)
|
|
906
|
+
exit 0 models, or a declared and proven style · 1 heroes are code · 2 not
|
|
907
|
+
observed · 3 error
|
|
908
|
+
set|patch|report: the HUD and screen look as one value, src/look.json (the UI role
|
|
909
|
+
tokens styles.css reads: bg, ink, plate, plate-ink, accent, font-display …; the
|
|
910
|
+
starter's src/look.ts applies it at load). set token=value and patch file.json
|
|
911
|
+
(a merge patch: null removes a token) validate the merged whole and refuse to write
|
|
912
|
+
on a problem, with the closest token names; every write and report prints the
|
|
913
|
+
contrast of each text/background pair and, for one that fails, the ink that passes
|
|
914
|
+
(a proposal: run the printed \`look set\`). report reads the file as the game does
|
|
915
|
+
(.funtools/look/report.json)
|
|
916
|
+
exit 0 written / clean · 1 refused, or tokens the game would drop · 2 no look · 3 error
|
|
917
|
+
play the blind player's hands and eyes: a round keeps one page of the built game open;
|
|
918
|
+
\`act\` plays a batch of steps (up to 20) as a person would and saves the screen once,
|
|
919
|
+
after the batch (tap X Y · drag X1 Y1 X2 Y2 [ms] · hold X Y ms · hold KEY ms · press
|
|
920
|
+
KEY [n] · wait ms · look DX DY [ms] · view NAME · verb NAME · shot; 0.5 is a fraction of
|
|
921
|
+
the screen, 200 a pixel; up/down/left/right are the arrows; a phone takes touch only;
|
|
922
|
+
"A & B" is one gesture, two thumbs at once). \`hold KEY|X Y until COND [max N]\` and
|
|
923
|
+
\`wait until COND [max N]\` let the game run in real time until a field of
|
|
924
|
+
__FPX_STATE__ says so (screen!=playing, x>120, score>=3) or the screen (\`change\`, or
|
|
925
|
+
\`change X1 Y1 X2 Y2\` for a box) visibly changes, 10 s by default, 30 s at most; the act
|
|
926
|
+
says whether and when, and the log keeps how long it ran. \`look DX DY\` moves the
|
|
927
|
+
mouse with no button down (camera look, under pointer lock too, which play grants as a
|
|
928
|
+
browser does; a drag clicks). \`play look\` (or \`act --look\`) hands back a small frame
|
|
929
|
+
(NNN-look.jpg) and the state as one line, no game time passing, no step spent. A
|
|
930
|
+
helper script the player writes belongs in its round's folder: a command run from one
|
|
931
|
+
elsewhere under the game, or in a temp folder, is refused. The
|
|
932
|
+
game's clock is held between acts; \`report\` files the player's report (start,
|
|
933
|
+
goal, act, end, confusions, each funpixel.json criterion, top 3 defects) with each
|
|
934
|
+
defect's exact repro from the round's log, and closes the round; \`check\` names
|
|
935
|
+
each field a draft gets wrong (defects[0].step: got "9a"; expected …) without
|
|
936
|
+
filing; a refused report is kept as written in round-N/report-unfiled.json, and a
|
|
937
|
+
round that went idle still takes its report. Each round has its own session in
|
|
938
|
+
.funtools/play/round-N/ (two may be open at once, a blind pair in one folder or
|
|
939
|
+
two): later commands find it in --dir, else the game folder at or above the
|
|
940
|
+
current one, and --round N (printed at open) names it when two are open there.
|
|
941
|
+
The player never reads the source. At most --rounds (3) rounds, then the panel.
|
|
942
|
+
--long N: a long session of about N minutes of game time (a bigger step budget),
|
|
943
|
+
ending with the come-back question: "would you come back tomorrow, and why?"
|
|
944
|
+
exit: report 0 a new player gets through · 1 blocked · 3 error; check 0 would file · 3 not
|
|
945
|
+
journey a player's path from the visible start through the real controls, kept as a script
|
|
946
|
+
(test/journeys/<name>.json: play's steps without view/verb jumps, plus
|
|
947
|
+
\`mark NAME field=value score>0 …\` checkpoints) and replayed on the same seed
|
|
948
|
+
(window.__FPX_SEED__) and viewport: at each mark the clock is held, the frame kept
|
|
949
|
+
and __FPX_STATE__ read into counters and judged. \`record\` opens a window you
|
|
950
|
+
play in (your taps, drags and keys become the steps; each screen reached becomes a
|
|
951
|
+
mark); \`from-play\` takes a blind player's round; \`replay\` sets the run beside the
|
|
952
|
+
last one (--repeat 2: two runs of one build, the noise); \`diff\` any two runs.
|
|
953
|
+
Kept apart from __FPX_VIEWS__, the diagnostic scenes: a scene reproduces a state, a
|
|
954
|
+
journey shows a player can reach it. Runs in .funtools/journeys/<name>/<n>/
|
|
955
|
+
replay exit 0 every expectation held · 1 a step or one failed · 2 nothing judged · 3 error
|
|
956
|
+
playtest the creator's mirror between rounds: builds, verifies, captures every view at both
|
|
957
|
+
sizes (moments as strips), audits the phone screens, judges the HUD in play and
|
|
958
|
+
profiles, all free; then writes one prompt per seat for your own agent (the
|
|
959
|
+
calibrated critic panel, 3 seats or 2 with --quick, each re-reading its axes on the
|
|
960
|
+
phone captures; a blind phone player round; a
|
|
961
|
+
head to head against the genre hit when its evidence is on this machine, never
|
|
962
|
+
shipped: --versus <dir> or FUNPIXEL_HITS_DIR/<slug>/). \`report\` ranks 3–4 next
|
|
963
|
+
steps, each with a quote, a frame and a number, and sets this playtest beside the
|
|
964
|
+
last (.funtools/playtest/<n>/report.md). --hosted runs the seats as Tools API jobs
|
|
965
|
+
(review, play) for credits, quoted first. Beside the craft panel: a plan-fidelity seat
|
|
966
|
+
(the GDD's target frames beside the game's frames of the same moments) and, after the
|
|
967
|
+
blind round (a long session, --long 10 by default, 0 for a short one), \`experience\`
|
|
968
|
+
measures depth and writes the experience seats (depth, content, replay, story, world,
|
|
969
|
+
arc, originality). A Mobile QA seat judges whether it is native to a phone (--phone
|
|
970
|
+
runs \`phone\` first, so it sees real Mobile Safari). Nothing is ever applied to the game
|
|
971
|
+
The panel informs and never gates: \`report --min-score 6.5\` exits 1 under the number
|
|
972
|
+
you ask for, and funpixel.json owner_gates.panel_min makes it the owner's hold.
|
|
973
|
+
\`report\` also keeps the do-not-regress list (.funtools/keep.json): up to eight things
|
|
974
|
+
the panel named as working; the next ledger seat judges each held, lost or not
|
|
975
|
+
observed, and a lost one is a regression: the report leads with it and exits 1, and
|
|
976
|
+
the panel stage fails. \`keep\` lists it; only the owner drops an item (--drop K<n> --by owner)
|
|
977
|
+
mobile-qa is the game native to a phone? One seat for your own agent, judged from phone
|
|
978
|
+
evidence: real Mobile Safari (the latest \`phone\` runs; --phone makes them), a WebKit
|
|
979
|
+
phone shot, the Chromium phone frames, audit-screens and look --hud at phone size, key
|
|
980
|
+
parity at phone size and the blind phone round. It judges touch-native controls (a
|
|
981
|
+
button that repeats the play area's gesture, never needed controls), the play area's
|
|
982
|
+
share of the screen, thumb reach, tap targets, chrome during play, safe areas,
|
|
983
|
+
orientation, readability and first touch; high/medium/low with a frame and a number.
|
|
984
|
+
\`report\` ranks the answer. Runs in .funtools/mobile-qa/<n>/; playtest runs the same
|
|
985
|
+
seat. Declare "platform": "phone" in funpixel.json metadata when the phone is primary
|
|
986
|
+
(a key with no touch route is then high); "desktop" opts out. Advisory
|
|
987
|
+
exit 0 prepared/reported · 1 build failed · 2 not a phone game · 3 error
|
|
988
|
+
exit 0 prepared or reported · 1 the build failed · 3 error
|
|
989
|
+
lessons settled findings by genre: after a fix round, \`add\` records what the panel or the
|
|
990
|
+
player named and what fixed it (in .funtools/lessons.jsonl; --share also in
|
|
991
|
+
~/.funpixel/lessons/ for the next game of the genre on this machine); with no
|
|
992
|
+
\`add\`, the best for the game's genre (the GDK's session start shows the top three)
|
|
993
|
+
and the motifs of the last five other games. \`motif\` records this game's
|
|
994
|
+
look (title, palette roles, motif nouns of the design note) in
|
|
995
|
+
~/.funpixel/lessons/motifs.jsonl, so the next game avoids it
|
|
996
|
+
variety is this game too close to the other games on this machine (the ones in
|
|
997
|
+
~/.funpixel/lessons/motifs.jsonl)? Five signals, each three-state: palette distance
|
|
998
|
+
(ΔE), the same core verb family in the same genre, a near-identical title screen
|
|
999
|
+
(edge layout of the latest title capture, so one template in new colours still
|
|
1000
|
+
counts), shared motif nouns, a shared title word, the same signature wow expressed
|
|
1001
|
+
alike (the GDD's ## Wow). Other folders of this same game
|
|
1002
|
+
are skipped. Records this game's verb and title layout for the next one. A mirror:
|
|
1003
|
+
the creator decides what to change. --within compares this game's own place
|
|
1004
|
+
pictures (design/first-look/place-*, or those ## Places names) with each other:
|
|
1005
|
+
colour and shape per pair, so six places that read as one room repainted warn
|
|
1006
|
+
exit 0 nothing too close · 1 something is · 2 nothing to compare · 3 error
|
|
1007
|
+
(--within: 0 compared · 2 under two pictures)
|
|
1008
|
+
wow the genre card's wow catalogue: hit-proven moves in four families (signature, juice,
|
|
1009
|
+
feel, presentation), each with the hit, what to build (numbers where a source gives
|
|
1010
|
+
them) and its check (a probe event the game marks with __FPX_MOMENT__, or a frame of
|
|
1011
|
+
a view). \`wow pick\` records the game's choice in the GDD's ## Wow (one signature, up
|
|
1012
|
+
to three more, each with its expression: what this game makes of the move) and flags
|
|
1013
|
+
a signature another game on this machine already used, expressed alike. \`wow check\`
|
|
1014
|
+
reads each pick's evidence: the event in the last hook read, the view's score
|
|
1015
|
+
exit 0 listed / picked / all seen · 1 a pick's evidence missing · 2 not observed · 3 error
|
|
1016
|
+
pitch the studio pitch: pitch → pick → lock → go hard. Cards (genre and fidelity, depth, the
|
|
1017
|
+
core that burns, story standard, extras with their cost) in design/studio.json;
|
|
1018
|
+
\`propose\` sets the agent's pick with its reason for this game (a new option joins the
|
|
1019
|
+
card with --label/--picture/--cost), \`ask\` puts each card the owner has not settled to
|
|
1020
|
+
them (a taste, spend or scope question in the chat), \`pick\` records a pick (--by owner
|
|
1021
|
+
is the owner's), \`lock\` writes the picks to funpixel.json "studio" (and
|
|
1022
|
+
production.tier from the depth) and the GDD's ## The studio pitch, and exits 1 while a
|
|
1023
|
+
card waits for the owner (unless the owner set autonomy go); \`gdd\` and the
|
|
1024
|
+
session start then read the locked core. Autonomy is never a card: the owner's
|
|
1025
|
+
funpixel.json flow.autonomy. No subcommand: the cards and what the
|
|
1026
|
+
locked layer still asks of the GDD. \`widen\`, before the pick: a seeded, replayable
|
|
1027
|
+
spread (operators × domains × facets, near/middle/far, one labelled wildcard; the seed is
|
|
1028
|
+
printed, --seed replays it) into design/widen.json for the agent to answer; \`widen score\`
|
|
1029
|
+
measures each answer against the generic answer for the brief and what the wow ledgers
|
|
1030
|
+
kept; \`widen keep W2\` records the kept card so the next widen of this brief steers away
|
|
1031
|
+
exit 0 shown or written · 2 no game folder · 3 error
|
|
1032
|
+
loudness every cue against its loudness target with the local ffmpeg (EBU R128): one-shot
|
|
1033
|
+
−16, bed −22, music and voice −18 LUFS ±3, true peak ≤ −1 dBTP, silent takes named.
|
|
1034
|
+
Measures, never changes a file. No paths: the game's sound folders. A bed or
|
|
1035
|
+
music track (what a game loops) also has its join measured: a gap (silence at the
|
|
1036
|
+
ends) or a click (the last sample jumping into the first) warns, and is the zero
|
|
1037
|
+
board's audio seams
|
|
1038
|
+
exit 0 measured · 2 nothing measured (no ffmpeg, no audio) · 3 error
|
|
1039
|
+
beats each music track's beat map written beside it as <track>.beats.json: tempo,
|
|
1040
|
+
first beat, downbeat, every beat time and how clear the pulse is, from the
|
|
1041
|
+
track itself (WAV read directly, others through ffmpeg); a track with no clear
|
|
1042
|
+
pulse says so instead of inventing one. The starter's src/beat.ts reads it
|
|
1043
|
+
(loadBeats, beatAt, nearestBeat, nextBeat) against sound.ts musicTime()
|
|
1044
|
+
exit 0 written · 2 nothing read · 3 error
|
|
1045
|
+
schema the whole game held to one shape: funpixel.json (every key a kit command
|
|
1046
|
+
reads, its type and values) and the plan files beside it (src/look.json,
|
|
1047
|
+
assets/assets.json, design/pipeline.json, design/spec/tunables.json, the
|
|
1048
|
+
forged world), each alone and against the others; an unknown key is a
|
|
1049
|
+
warning with the closest name (--strict: an error, what a writer refuses on)
|
|
1050
|
+
exit 0 no errors · 1 errors · 3 error
|
|
1051
|
+
loop a generated video (gen video, from the accepted sprite) or a folder of frames
|
|
1052
|
+
made into a seamless sprite cycle: sampled at --fps, the --min..--max frame
|
|
1053
|
+
window whose last frame leads back into its first as smoothly as an ordinary
|
|
1054
|
+
step, keyed, every frame cut to one shared box (no jitter), packed as a sheet
|
|
1055
|
+
PNG + spritesheet JSON with the cycle as an animation. Free (ffmpeg for video)
|
|
1056
|
+
exit 0 written (a visible seam warns) · 2 too few frames or no ffmpeg · 3 error
|
|
1057
|
+
directions a model's orbit views (gen render: turn_00.png …) made into an 8-way or 4-way
|
|
1058
|
+
sprite set: angle 0 faces the viewer (S), the rest named going round
|
|
1059
|
+
(--reverse for the other way), keyed off the stage, one shared box, a sheet
|
|
1060
|
+
PNG + JSON with an animation per direction. Repainting each view in the
|
|
1061
|
+
game's style is a paid edit per view: printed, never run
|
|
1062
|
+
exit 0 written · 2 too few views · 3 error
|
|
1063
|
+
board the first look's reference board (design/board/): search open-licence
|
|
1064
|
+
collections (Openverse, free, no key), wander from a kept picture to related
|
|
1065
|
+
ones, or add your own files and links; every kept picture names the one or
|
|
1066
|
+
two things it lends (palette, shape, light, composition, material, motion,
|
|
1067
|
+
mood, type) and its licence. \`show\` writes board.html and fails when a board
|
|
1068
|
+
picture is copied into public/ or assets/: a reference informs, never ships
|
|
1069
|
+
exit 0 · 1 a board picture would ship · 2 nothing found · 3 error
|
|
1070
|
+
zero the zero board (design/status.json \`board\`): the loop's counters as the commands
|
|
1071
|
+
that measured them left them, each with its trend over the last runs and what it
|
|
1072
|
+
is made of — errors (verify's failing gates + distinct page errors, verify and
|
|
1073
|
+
capture --all), overlaps (controls cut or covered, HUD over HUD, under the notch:
|
|
1074
|
+
audit-screens per surface, which check runs), seams (looping sprite cycles that pop,
|
|
1075
|
+
look --sheet; looping music and beds that click or gap at the wrap, loudness) and
|
|
1076
|
+
frame time (profile's worst p95 per viewport, against its budget, 18 ms unless
|
|
1077
|
+
--budget); and comfort (score: moments whose frame is too busy or clipped), which
|
|
1078
|
+
warns and never fails. A counter nothing measured is not observed, never 0. Draw, render,
|
|
1079
|
+
check, fix, repeat until it reads zero. Measures nothing itself. Each counter's parts
|
|
1080
|
+
are shown by trade (the game's own; review's video, audio, model, picture)
|
|
1081
|
+
exit 0 every observed counter zero (frame time within budget) · 1 not · 2 nothing observed · 3 error
|
|
1082
|
+
review the zero board for every trade, not only games: measures media (a film spot, a
|
|
1083
|
+
music single, a podcast, a 3D model, a picture series, a game's assets) with the local
|
|
1084
|
+
ffmpeg and the kit's own readers, stamps each trade's part of errors, overlaps and
|
|
1085
|
+
seams in the project's design/status.json (one with no status gets one holding the
|
|
1086
|
+
board), and prints it. Video: a stream that does not decode, black frames outside the
|
|
1087
|
+
first and last 0.5 s, frozen spans over --freeze (2 s) on a moving cut (--still: a
|
|
1088
|
+
still cut), captions outside the 9:16 / 16:9 / 1:1 safe zone when a .vtt/.srt/
|
|
1089
|
+
.captions.json beside it says where they sit, a loop's last frame into its first
|
|
1090
|
+
(named loop, or --loop), loudness as a note (−14 LUFS web, −23 broadcast). Audio:
|
|
1091
|
+
clipping (true peak over −1 dBTP), silence (a dud, a dropout of 2 s+ inside it), a
|
|
1092
|
+
loop's or bed's join (gap or click), loudness as a note (a game's cue classes, else
|
|
1093
|
+
−14 music, −16 speech; --lufs sets it). 3D (glb/gltf, read here, never the hosted
|
|
1094
|
+
bench): missing or broken textures, open or non-manifold edges (welded by position),
|
|
1095
|
+
NaN positions, triangles over a budget (--triangles, funpixel.json review.triangles,
|
|
1096
|
+
design/specs); Draco/meshopt geometry is not decoded (its edges not observed).
|
|
1097
|
+
Pictures: does not decode, a sprite or cut-out with no alpha (by its name, the asset
|
|
1098
|
+
manifest, a spec, --alpha), a size off its spec (design/specs, --size), off the
|
|
1099
|
+
project's palette (only when it has one), a tile whose edges do not meet. Every
|
|
1100
|
+
counter is three-state: one a file cannot answer is not observed, with why, never
|
|
1101
|
+
0. The project is the nearest folder with design/ or funpixel.json at or above the
|
|
1102
|
+
path (--dir names it); review keeps .funtools/review.json so one file's re-review
|
|
1103
|
+
replaces only its own counts. Folders skip node_modules, dist, build and raw
|
|
1104
|
+
exit 0 every observed counter zero · 1 one is not · 2 nothing observed (no media, no ffmpeg) · 3 error
|
|
1105
|
+
journal the creator's journal: one line per change a PERSON made (by hand, in the editor, a
|
|
1106
|
+
tuned number), with who and why, in .funtools/journal.jsonl (never ships). The GDK's
|
|
1107
|
+
session start shows the recent ones so the agent keeps them; it also records files
|
|
1108
|
+
that changed while no agent turn ran, marked detected (who and why unknown). Run in
|
|
1109
|
+
an agent's shell the line says via the agent. No text: the entries
|
|
1110
|
+
exit 0 recorded or listed · 2 nothing recorded · 3 error
|
|
1111
|
+
decide the decisions log: one choice against its alternative, with the reason, as D<n> in
|
|
1112
|
+
design/decisions.md (never ships). --supersedes D3 replaces one and keeps both on
|
|
1113
|
+
the record. The GDK's session start lists the live ones; \`gdd\` reads them. No
|
|
1114
|
+
text: the decisions, live first. --from N3: the owner's note becomes a decision citing
|
|
1115
|
+
their words and screenshot, open until a lane builds it (the briefs that touch it carry it)
|
|
1116
|
+
exit 0 recorded or listed · 2 none yet · 3 error
|
|
1117
|
+
lanes a stage split into lanes that run at once, each in its own git checkout (branch lane/<id>):
|
|
1118
|
+
\`plan\` proposes them (play, look, feel, audio, env, room-<id>) and writes each brief (scope,
|
|
1119
|
+
what it owns and stays out of, the owner's open decisions that touch it, the evidence that
|
|
1120
|
+
closes it, a $ cap, a time-box, 45-min checkpoints, how to hand back and report); \`start\`
|
|
1121
|
+
makes the checkout and prints the host call (Claude Code: one Agent call per lane). Paid
|
|
1122
|
+
funtools calls in a lane's checkout are logged against it and refused past its cap. \`done\`
|
|
1123
|
+
runs the gate on the lane's commit (synced, owns, tests, sim, the look floor 75 on its
|
|
1124
|
+
frames, funpixel.json lanes.gates commands such as a spec lint, a checkpoint); a seat that
|
|
1125
|
+
did not build it records \`verdict\`; \`merge\` is refused until the gate, the verifier and
|
|
1126
|
+
the owner's picks pass, then merges --no-ff and writes design/MAKING.md
|
|
1127
|
+
exit 0 done · 1 refused · 2 no lanes · 3 error
|
|
1128
|
+
pick the owner picks by eye between two to four candidate frames, shown in the chat; the choice is recorded
|
|
1129
|
+
with each frame above its button; it never defaults, the lane it names cannot merge until
|
|
1130
|
+
the owner answers, and the choice becomes a decision in design/decisions.md
|
|
1131
|
+
exit 0 done · 1 not the owner's word · 2 no picks · 3 error
|
|
1132
|
+
flow one entry for the whole run: where it is (planning, the plan put to the owner, round N on a
|
|
1133
|
+
stage), the ONE next step with the exact command, and why, recorded in .funtools/flow.json.
|
|
1134
|
+
\`flow start\` makes the game (init) from the brief (its title, kind and size read from the
|
|
1135
|
+
sentence) and asks nothing. The plan is made with the owner in the chat (the GDD skill);
|
|
1136
|
+
\`flow ask\` puts it to them once with its price (the asset script at list price) and what it
|
|
1137
|
+
still owes, as warnings, never a refusal; \`flow agree --by owner "<their words>"\` locks it and
|
|
1138
|
+
the approved price becomes the budget. Then round after round on the build: the look-dev loop
|
|
1139
|
+
(capture, compare --target, fix the biggest difference, play), a card with a frame of the
|
|
1140
|
+
build, the owner's notes picked up; \`flow round\` closes a round only on that evidence. The
|
|
1141
|
+
GDK's Stop keeps the rounds going up to funpixel.json flow.rounds_per_session (default 3), then
|
|
1142
|
+
hands back ("resume with /fp:flow"). \`flow ask\` and the cards carry the kit's own read (score,
|
|
1143
|
+
panel, blind player, world, first look): "Below the bar: …" first, "Not judged yet" when
|
|
1144
|
+
nothing ran. \`flow drop --by owner\` ends the game: notes close as dropped, \`flow\` names no step.
|
|
1145
|
+
After the first playable and every few rounds (funpixel.json flow.look_every, default 3; 0
|
|
1146
|
+
is off) the step is the whole-game look: \`flow look\` writes its brief for a fresh agent
|
|
1147
|
+
(would you ship this today? each defect a severity and an owner) and, once filed, routes
|
|
1148
|
+
each defect to the lane of its kind, the owner or the round. It never vetoes
|
|
1149
|
+
exit 0 a step · 1 the round lacks evidence (or agree without --by owner) · 2 not a game folder · 3 error
|
|
1150
|
+
owner the owner's standing bar, carried to every new game: short lines in ~/.funpixel/owner.md (the
|
|
1151
|
+
person's) and an optional <repo>/.funpixel/owner.md (the team's). \`flow start\` (and each
|
|
1152
|
+
\`flow\`) matches it to the game, never blindly: a line that applies becomes an owner note,
|
|
1153
|
+
open until evidence closes it; one that does not is skipped with a reason the owner reads
|
|
1154
|
+
("creatures: a one-screen word puzzle has no world"); one the kit cannot tell is kept as
|
|
1155
|
+
applying, with why. design/owner-bar.json keeps the match; apply/skip flip a line.
|
|
1156
|
+
seed writes the owner's requests of 4 Oct
|
|
1157
|
+
exit 0 done · 1 nothing matched · 2 no bar yet · 3 error
|
|
1158
|
+
firstlook the first look's required set, the whole game: key art; every screen (title as the play
|
|
1159
|
+
world, pause, the end; past small settings, results, a level select); the HUD on the phone
|
|
1160
|
+
and the desktop; a frame per place tied to a story beat and a living-world sheet (a world
|
|
1161
|
+
game only); an animation board, a juice board, the sound list, the fun page. Scaled by tier
|
|
1162
|
+
and by the game; the first-look gate and \`flow ask\` wait on it; the owner drops an item with
|
|
1163
|
+
skip --by owner. Shown in the chat, grouped. render: the code-drawn route, the studies the
|
|
1164
|
+
game's own draw code composes on design/first-look/draw.html (window.__FPX_FIRSTLOOK__ resolves
|
|
1165
|
+
to { name: PNG data URL }), served by the game's Vite and written to design/first-look/<name>.png
|
|
1166
|
+
exit 0 complete · 1 items missing · 2 not a game folder · 3 error (render: 0 written · 1 none or
|
|
1167
|
+
a refused one · 2 no headless browser · 3 error)
|
|
1168
|
+
note the owner's notes on the stage they affect, in design/pipeline.json: add (the owner's words,
|
|
1169
|
+
--screenshot as the named before-frame), close with evidence that exists (a file in the
|
|
1170
|
+
game, a commit, a test name in test/; a design note: a commit that changes design/) or
|
|
1171
|
+
on the owner's word (--by owner --force),
|
|
1172
|
+
verdict accept|reject (the owner's: outranks any agent claim; a reject reopens a closed note; on a stage it is that stage's owner gate)
|
|
1173
|
+
exit 0 done · 1 the evidence does not exist · 2 no notes · 3 error
|
|
1174
|
+
progress the run's record, in .funtools/progress.jsonl (read by the next session and the hand-back): a
|
|
1175
|
+
card at each stage move with a frame from the built game (--shot last = the newest capture; a
|
|
1176
|
+
card with no frame or evidence is kept and marked not observed, a design/ picture is marked "a
|
|
1177
|
+
picture, not the build"); a heartbeat when a stage runs long (the third on one gate becomes a
|
|
1178
|
+
stuck card asking skip / cut / keep going); questions only at forks (taste, spend, scope), asked
|
|
1179
|
+
in the chat and answered with \`progress answer\` in the owner's words, with a default that lands
|
|
1180
|
+
after its timeout under autonomy go ("decided without you; reply to reverse"); --spent past the
|
|
1181
|
+
approved budget makes a pause card. Each card picks up the owner's new notes and answers.
|
|
1182
|
+
report is the hand-back from the cards. FUNPIXEL_NOTIFY_URL (ntfy, Slack, Discord) hears only
|
|
1183
|
+
"needs you" and "done"
|
|
1184
|
+
exit 0 done · 2 nothing yet / still open · 3 error
|
|
1185
|
+
feedback what creators said about the kit: \`collect\` reads every .funtools/devlog.jsonl and
|
|
1186
|
+
.funtools/kit-feedback.jsonl under the folders (eval copies of one game count once) and
|
|
1187
|
+
ranks what worked, what hurt and what was missing, by tool, with counts, the games
|
|
1188
|
+
and quotes; lines about the game only are counted, not ranked. Reads, never uploads
|
|
1189
|
+
exit 0 reported · 2 no feedback found · 3 error. \`log\` appends one dev-log line,
|
|
1190
|
+
stamped, to the game's .funtools/devlog.jsonl (no heredoc or >> needed). Beside the kit tools,
|
|
1191
|
+
collect ranks the GAME's practice: the owner first (owner lines and each game's pipeline
|
|
1192
|
+
notes outrank any agent claim), what worked by its measured change (--before/--after, or
|
|
1193
|
+
"34 -> 25.5 ms" in the words; a lesson that says it fixed something counts), each entry's
|
|
1194
|
+
evidence checked to exist (a file, a commit, a test; else unverified), dead ends by the
|
|
1195
|
+
hours they burned, cost and time per game, and A/B records (.funtools/ab.jsonl) as "GDK
|
|
1196
|
+
lost level design by 1.8 in 1 of 2". --propose drafts one kit edit per top recurring lesson;
|
|
1197
|
+
--html writes one page with filters. \`ab\` records an A/B; \`merged\` writes the merge
|
|
1198
|
+
commit as a worked entry with the numbers the tools logged since its merge base (sim,
|
|
1199
|
+
play, hook, life, profile and playtest log theirs as measure lines on their own)
|
|
1200
|
+
character the character library: characters animated already. list shows the shelf
|
|
1201
|
+
(2D sheets with idle, walk, run, jump, attack, hit, death and blink; 3D rigged GLBs
|
|
1202
|
+
with every clip by name on one humanoid rig); get copies one into
|
|
1203
|
+
public/assets/characters/<id>/ for free; restyle gives it your look (--game-id:
|
|
1204
|
+
your style lock) and keeps every motion, priced per frame (--animations: fewer)
|
|
1205
|
+
pack exactly what an upload carries, a leak scan, the server's limits and a
|
|
1206
|
+
build from the staged copy; never uploads (exit 1 = must not be uploaded)
|
|
1207
|
+
export the game as a clean folder of build inputs for another repo: index.html, src/,
|
|
1208
|
+
public/, package.json + lock, tsconfig, vite.config, funpixel.json cut to name,
|
|
1209
|
+
splash, metadata, checks and hooks, a two-line .harnessignore; notes, design,
|
|
1210
|
+
evidence, agent files, tests, scripts and the Funpixel splash card (unless
|
|
1211
|
+
"splash": "funpixel") stay behind. Every source comment is removed (the
|
|
1212
|
+
game's TypeScript parser and printer; CSS too), then the dev folder and the clean
|
|
1213
|
+
folder are both built and the bundles must be byte-identical. Reports leaks
|
|
1214
|
+
(secrets, home paths, tool and provider names), absolute /public URLs in code, a
|
|
1215
|
+
dev .harnessignore that excluded a build input, then runs the clean build: verify
|
|
1216
|
+
at both sizes, audit-screens phone portrait and landscape, and every request the
|
|
1217
|
+
page makes on every view (a 404 = a file the folder lost). Mobile findings (viewport
|
|
1218
|
+
meta, safe area, hover-only UI, keys with no touch route, small text and targets)
|
|
1219
|
+
are the game's to fix and never block. --check reports and writes nothing.
|
|
1220
|
+
--strip-metadata removes text chunks and C2PA content credentials from public
|
|
1221
|
+
PNGs, where a generator writes its own name (the owner's call: off by default)
|
|
1222
|
+
exit 0 clean (written) · 1 a problem, nothing written · 3 error
|
|
1223
|
+
gen generate an asset with the Tools API, follow the job and save the files into
|
|
1224
|
+
the game (default public/assets/images|sprites|sfx|music|models/, the folders
|
|
1225
|
+
the scaffold reads); --cast <id> makes it from that cast member's master (reference 1
|
|
1226
|
+
of a picture with its keep-line, the source of an edit or sheet, the image of a model)
|
|
1227
|
+
and holds the result to it by numbers when it lands (cast check); inspect
|
|
1228
|
+
measures a model you made, optimize-model decimates it to a budget, fracture
|
|
1229
|
+
breaks it into shards, lod makes its
|
|
1230
|
+
levels of detail, rig authors clips onto a bare biped rig (or fits a
|
|
1231
|
+
donor's rig onto a bare mesh) after a free rig check, render photographs it
|
|
1232
|
+
(views round it, a front view, a contact sheet), finalize repairs broken
|
|
1233
|
+
materials, drops clips not kept and renders the proof; pictures go to
|
|
1234
|
+
.funtools/renders/<name>/, never shipped; texture is a seamless tile and strip
|
|
1235
|
+
a scrolling layer (refused, uncharged, if it shows a seam); skybox is a 360°
|
|
1236
|
+
equirect sky; find-material searches Poly Haven's free CC0 library and
|
|
1237
|
+
material copies one asset (HDRI, texture set, glTF model) into
|
|
1238
|
+
public/assets/materials/<name>/, its licence and source recorded; video makes
|
|
1239
|
+
a 5 or 10 s clip (generated, never gameplay: for trailer cards) into .funtools/video/;
|
|
1240
|
+
--camera-move static|pan_left|pan_right|tilt_up|tilt_down|dolly_in|dolly_out|orbit_left|orbit_right|
|
|
1241
|
+
crane_up|handheld, --lens wide|normal|tele|macro, --aperture deep|shallow, --camera-speed slow|normal|fast:
|
|
1242
|
+
camera words the model takes (its own control or a fixed phrase; one it does not take is refused);
|
|
1243
|
+
lipsync --image-asset <portrait> --audio-asset <line.mp3> [--duration <s>] [--resolution 480p|720p] makes
|
|
1244
|
+
the portrait speak the line (up to 60 s, priced per second; the portrait must be your own face or a
|
|
1245
|
+
character you made, never a real person who has not agreed) into .funtools/video/;
|
|
1246
|
+
dialogue <scene.json> speaks a scene (an array of {speaker, text, voice?, tags?, pause_after_ms?,
|
|
1247
|
+
track?}, or {lines, cast?, gap_ms?}; a voice is a preset or a cast voice.json; --gap ms, default 250)
|
|
1248
|
+
into public/assets/voice/<scene>/: dialogue.mp3 (one mixed file), line-NN.mp3, dialogue.json (the
|
|
1249
|
+
timeline: tracks by speaker, each clip's start and length); priced as its lines
|
|
1250
|
+
--model <id> on image, sprite, sound, music, voice, line, dialogue, video, edit-video, lipsync and model3d: a model
|
|
1251
|
+
from the registry (the ids: GET /v1/tools/models, or an unknown one's refusal, which lists
|
|
1252
|
+
them); omitted, our recommended pick runs, with its fallback; named, it runs alone, never swapped.
|
|
1253
|
+
voice, line and dialogue also take --model kokoro-82m: Kokoro on this Mac (Apple Silicon, free, offline, slower,
|
|
1254
|
+
not checked by us; set up once with funtools models install kokoro-82m, else exit 2 naming it)
|
|
1255
|
+
options: image/texture/strip/sprite --size WxH, snapped to the nearest of 1:1 3:2 2:3 4:3 3:4
|
|
1256
|
+
16:9 9:16 21:9 (1024x1024, 1536x1024 → 3:2, 1024x1536 → 2:3, 1792x1024 → 16:9, 1024x1792 → 9:16;
|
|
1257
|
+
the model picks the pixels for the shape); --quality low|medium|high: an image is
|
|
1258
|
+
2 · 4 · 12 credits (low · medium, the default · high; a square, the dearest shape;
|
|
1259
|
+
funtools estimate image prints the exact figure before you spend)
|
|
1260
|
+
--target-size px (the file's longest side: the size the game shows it at; scaled down on
|
|
1261
|
+
the server after drawing, never up, so a 60 px sprite does not ship at 1024²),
|
|
1262
|
+
image/strip --transparent; skybox --negative "what not to show";
|
|
1263
|
+
find-material --type hdri|texture|model --limit 1-20;
|
|
1264
|
+
material --type hdri|texture|model (--id id | "keywords") --resolution 1k|2k;
|
|
1265
|
+
sound --duration secs --loop; music --length clip|full --instrumental;
|
|
1266
|
+
model3d --image-url https://… | --image-asset <file|id> [--back-asset --left-asset
|
|
1267
|
+
--right-asset <file|id>: more sides of the subject, so its back is drawn, not invented],
|
|
1268
|
+
--style standard|hero|lowpoly (standard ≈30K triangles; hero is static only: 300K triangles
|
|
1269
|
+
on Meshy 7.1 at 4K geometry, 500K faces on Hunyuan 3.1), --model meshy-7.1|hunyuan-3.1|rodin-2.5
|
|
1270
|
+
(or --engine meshy|hunyuan|rodin, the older name: that engine alone, no fallback; Meshy ships
|
|
1271
|
+
2048 px maps, Hunyuan 4096 px PNG; rodin, only when named, is Rodin 2.5: static only,
|
|
1272
|
+
hard-surface props, every view in one job, 50K triangles and 2048 px PBR maps, hero 500K with
|
|
1273
|
+
4096 px maps, lowpoly 8K untextured; its prices are the rodin rows of funtools estimate), --raw (the
|
|
1274
|
+
engine's GLB byte for byte: every triangle, full maps, no optimizer pass, same price),
|
|
1275
|
+
--score name=ref.png,name=ref.png (when it lands: funtools fidelity on it, free; with
|
|
1276
|
+
--unity the engine's GLB is scored before it is split for glTFast),
|
|
1277
|
+
--animations walk,run; inspect --asset id; optimize-model --asset id
|
|
1278
|
+
--budget food|prop|thin_walled|set_piece|character [--device phone|desktop]
|
|
1279
|
+
[--height metres]; fracture --asset id [--pieces 2-32] [--seed n];
|
|
1280
|
+
lod --asset id [--levels 1-4] [--ratio 0.05-0.95] [--min-triangles n];
|
|
1281
|
+
rig --asset id [--clips idle,walk,attack,death | --donor id];
|
|
1282
|
+
render --asset id [--frames 1-12] [--size 64-1024]; finalize --asset id
|
|
1283
|
+
[--keep-clips Idle,Walk]; motion --asset id --motions '[{"kind":"wave"}]' (wave nod hop strike
|
|
1284
|
+
flinch celebrate, dials in tuned ranges) [--base-clip idle]; retarget --asset id
|
|
1285
|
+
(--donor id | --character robot-3d) [--only walk,run]; video --duration 5|10 [--aspect 16:9|9:16|1:1]
|
|
1286
|
+
[--image-asset id | --image-url https://…] (a still to animate)
|
|
1287
|
+
edit "the one change" --asset <file|id> [--mode edit|restyle|pose] [--game-id id]: the
|
|
1288
|
+
same picture changed, a cut-out kept a cut-out (a blink, a hurt face, a second step);
|
|
1289
|
+
sheet ["the character"] [--asset <sprite file|id>] --cycles idle:4,walk:6[@fps]
|
|
1290
|
+
[--view side|front, never from above: edit --mode pose] [--body biped|quadruped|flyer]
|
|
1291
|
+
[--frame-size 64-512] [--game-id id]
|
|
1292
|
+
(blink:1 is the eyes-shut swap; a flyer's walk and run fly): every frame an edit of one
|
|
1293
|
+
base, checked for drift, packed into public/assets/sprites/<name>/sheet.png + sheet.json
|
|
1294
|
+
(Pixi AnimatedSprite: sheet.animations.walk); priced per frame (look --sheet
|
|
1295
|
+
measures its loops). A picture prompt that names the game (funpixel.json name)
|
|
1296
|
+
is warned about: image models letter the name and garble it; --strip-title
|
|
1297
|
+
sends it without the name
|
|
1298
|
+
--unity <project> (a Unity project, or the folder holding its unity/): the files go into
|
|
1299
|
+
its Assets/_Game/Generated/<Images|Sprites|Textures|Materials|Skyboxes|Models|Sfx|Loops|
|
|
1300
|
+
Music|Voice>/ with an import rule (Generated/Editor/FunpixelGenImport.cs) that sets each
|
|
1301
|
+
kind's settings when Unity first imports it: sprites as Sprite, normal maps as NormalMap,
|
|
1302
|
+
ASTC by role, audio by length (SFX mono ADPCM, loops PCM, music Vorbis streaming); no
|
|
1303
|
+
.meta is written (Unity mints its GUID). A model is the engine's own GLB (--raw) split
|
|
1304
|
+
into .gltf + .bin + model__basecolor/__normal/__mr PNGs for glTFast; audio is 48 kHz WAV
|
|
1305
|
+
(ffmpeg). Takes image, sprite, strip, edit, sheet, texture, material, skybox, model3d,
|
|
1306
|
+
sound, music, line
|
|
1307
|
+
exit 2 also covers a key the server refused (unauthorized, insufficient_scope)
|
|
1308
|
+
exit 0 done · 1 the job failed · 2 refused before it ran (confirm with --yes,
|
|
1309
|
+
credits, the key's budget, the plan, rate limits) · 3 error
|
|
1310
|
+
estimate what a gen would cost, in credits, without running it
|
|
1311
|
+
models the generation models gen --model takes, from the registry: per kind our pick first (recommended,
|
|
1312
|
+
what runs when --model is left out), the other hosted models, then any that run on this Mac with
|
|
1313
|
+
whether they are set up here. install sets up a local one: kokoro-82m (a voice, Apple Silicon only)
|
|
1314
|
+
gets its runner with uv tool install (mlx-audio) and its weights into ~/.funpixel/models/ with a
|
|
1315
|
+
progress line. A local model is free and offline, slower and not checked by us; it runs only when
|
|
1316
|
+
named (gen voice|line|dialogue --model kokoro-82m), never in place of ours, and its files are recorded
|
|
1317
|
+
generated_by local:<id>. funtools doctor shows the same in its Local models block
|
|
1318
|
+
exit list 0; install 0 set up · 1 the install failed · 2 refused (a hosted id, needs Apple Silicon,
|
|
1319
|
+
no uv) · 3 error
|
|
1320
|
+
assets an optional pipeline for a game that makes many assets from text: items in
|
|
1321
|
+
assets/assets.json (name, kind image|sprite|sheet|texture|strip|sound|music|model3d,
|
|
1322
|
+
subject, family, role size, draws, atlas, anchors, depth, variants, from, params),
|
|
1323
|
+
each subject composed with assets/style.json (medium, the family's clause, never);
|
|
1324
|
+
.funtools/assets.lock.json records what made each file (request hash, job, asset,
|
|
1325
|
+
credits, sha256, the gate's verdict, the kept draw, earlier versions). status says
|
|
1326
|
+
fresh | stale | missing | rejected | edited per item, orphans and credits spent (free,
|
|
1327
|
+
offline). gen makes only missing and stale items in one generate_batch (--only,
|
|
1328
|
+
--family, --stale narrow it; the server keys each item by its content, so a rerun
|
|
1329
|
+
never pays twice), shows the estimate first, asks above 50 credits (--yes), stops
|
|
1330
|
+
at --max-credits; --dry-run shows every prompt and the estimate and sends nothing.
|
|
1331
|
+
draws: N keeps the first the gate accepts, all in .funtools/assets/draws/<name>/.
|
|
1332
|
+
regen re-makes one item and records --because in the lock and style.json failures.
|
|
1333
|
+
process keys (free key_sprite) and trims cut-outs, shrinks each to its role size,
|
|
1334
|
+
desaturates depth planes, packs atlas groups with the local optimizer and writes
|
|
1335
|
+
src/art/assets.json: relative URLs, anchors, a brightness tint per piece.
|
|
1336
|
+
A game declaring "pixelArt": { "scale": 4, "palette": ["#hex", …] } in funpixel.json
|
|
1337
|
+
has process (and gen) snap each picture to that grid and palette instead (hard
|
|
1338
|
+
alpha, one ink per art pixel, originals kept); pixels measures every PNG under
|
|
1339
|
+
public/assets/ (or the files named) for grid blocks that are not one colour, soft
|
|
1340
|
+
edges and inks off the palette, each three-state; --snap snaps the named files first
|
|
1341
|
+
exit 0 done · 1 something stale, missing or failed · 2 refused (confirm, cap) · 3 error;
|
|
1342
|
+
pixels: 0 on the lane · 1 off it · 2 not observed (no lane declared, nothing drawn)
|
|
1343
|
+
More kinds: pose (an edit of the cast master in the pose its subject names), rig (rig_model →
|
|
1344
|
+
author_motion "motions" → retarget_clips "retarget" on a made model3d, "from"), parts (cut
|
|
1345
|
+
from a "sheet" locally, free); a model3d's draws are a bake-off judged against its master
|
|
1346
|
+
(rendered with your Blender, free); "optimize": {device} runs optimize_model after.
|
|
1347
|
+
A picture of a cast member (its "cast", or a name that starts with the id) is drawn with
|
|
1348
|
+
the master attached first and the card's words in its prompt, then held to the master: a
|
|
1349
|
+
drift gets one targeted edit, then one redraw, then is kept as the best and named
|
|
1350
|
+
(lock: identity); the masters ride the first batch as the game's locked sheets ref-<id>.
|
|
1351
|
+
gen goes in batches of --batch (24), each priced before it is sent, and keeps to the
|
|
1352
|
+
owner's asset budget (assets budget) unless --max-credits says otherwise.
|
|
1353
|
+
script turns the plan's "## Asset script" rows (ID | What | Kind | Count | Cast) into
|
|
1354
|
+
manifest items (--write); budget records the owner's credits for them.
|
|
1355
|
+
cast the cast library: design/cast/<id>/ (master.png, turnaround.png, palette.json, card.json/md).
|
|
1356
|
+
add accepts a master; list names them; check holds pictures to a master by numbers
|
|
1357
|
+
(silhouette, colour, lost main colours): exit 0 on model · 1 drift · 2 not observed · 3 error
|
|
1358
|
+
job a job's status; --wait follows it to the end and saves its files (exit as gen)
|
|
1359
|
+
jobs your account's jobs, newest first, a page at a time; --tool takes a tool
|
|
1360
|
+
or a gen kind; a page with more prints the command for the next
|
|
1361
|
+
page; --refusals tallies, per tool, how often your own jobs were refused and by
|
|
1362
|
+
what (policy, a gate, bad input) over the latest 500: our routes measured, not a ranking
|
|
1363
|
+
optimize the Studio's asset optimizer on local files, offline and free. With no op: what
|
|
1364
|
+
the agent's optimization rule would flag (images of 400 KB or more, 4+ loose
|
|
1365
|
+
sprites); --check exits 1 on that (CI). With an op: a dry run unless --write,
|
|
1366
|
+
which also prints the brief your agent needs to change the loading code.
|
|
1367
|
+
optimize-mesh --levels phone,desktop,high cuts one GLB per device class beside
|
|
1368
|
+
the source (never replacing it) plus name.levels.json for the SDK's loadLevel;
|
|
1369
|
+
--class sets the triangle budget (skinned = character, else prop), --out-dir
|
|
1370
|
+
puts the levels elsewhere (a source kept outside public/). Output is
|
|
1371
|
+
Draco (static) or meshopt (rigged) with KTX2 textures; FUNPIXEL_GLB_KTX2=off
|
|
1372
|
+
keeps PNG/JPEG textures, FUNPIXEL_GLB_COMPRESS=draco|meshopt forces one codec
|
|
1373
|
+
(no uncompressed output yet: a page that cannot run the decoders needs a decode)
|
|
1374
|
+
playable the game as one ad network's playable: one HTML file in dist-playable/ (from
|
|
1375
|
+
playable.html when there is one), weighed against the network's ceiling, searched
|
|
1376
|
+
for anything it would fetch, then played offline on a phone with the network's
|
|
1377
|
+
call to action stubbed — it must fire on the end card, after the reward
|
|
1378
|
+
exit 0 pass · 1 fail · 2 not observed · 3 error
|
|
1379
|
+
trailer a store/social trailer from REAL play: the game filmed as reel films it,
|
|
1380
|
+
cut on the beat of its own music (the bed it loops, public/assets/music/, or
|
|
1381
|
+
--music) with its OWN sound effects where they played in the kept shots
|
|
1382
|
+
(--no-sfx leaves them out; synthesised sounds are listed, never faked), mixed
|
|
1383
|
+
to -16 LUFS / -1.5 dBTP, shots on its __FPX_MOMENT__ payoffs, a title card from its key art
|
|
1384
|
+
(design/key-art.png, or --key-art) and an end card; 16:9 and 9:16 MP4s and
|
|
1385
|
+
trailer.json (every segment's source) in .funtools/trailer/, never shipped.
|
|
1386
|
+
1:1 and 4:5 too; --for names where it is posted (tiktok, reels, shorts,
|
|
1387
|
+
stories, feed, square, youtube, store, site) and picks the formats; every
|
|
1388
|
+
card and label sits inside that format's clear area (the platform's own
|
|
1389
|
+
header, side rail and caption block never cover it: src/trailer/placement.ts).
|
|
1390
|
+
--intro/--outro add generated clips (gen video) as cards only, each labelled
|
|
1391
|
+
on screen as not gameplay. Needs ffmpeg. exit 0 written · 3 error
|
|
1392
|
+
reel a vertical share video of the game being played: the built game toured
|
|
1393
|
+
through its __FPX_VIEWS__ (title, play, ending) and driven by its own
|
|
1394
|
+
__FPX_VERBS__ (else its __FPX_KEYS__), filmed and cut to a 1080x1920 MP4
|
|
1395
|
+
with ffmpeg (default .funtools/reel/reel.mp4, never shipped); with no
|
|
1396
|
+
ffmpeg on PATH, or --frames-only, the PNG frames are the output
|
|
1397
|
+
motion the Motion bench: title cards, trailer shots, UI motion, logo stings and social loops for
|
|
1398
|
+
any trade (a single's visualiser, a podcast's audiogram, a film's titles, a game's trailer).
|
|
1399
|
+
A scene is a page that draws frame f (CSS, canvas, SVG or GSAP), which may say
|
|
1400
|
+
window.__MOTION__ = { seconds, fps, size, loop } and may draw any frame on demand with
|
|
1401
|
+
window.__MOTION_SEEK__(frame); flags beat it. render serves the scene (its folder as files,
|
|
1402
|
+
or its project's own Vite), opens it headless on the held clock (one frame = 1000/fps ms,
|
|
1403
|
+
Math.random seeded) and shoots every frame to <out>/frames/000001.png…, then encodes
|
|
1404
|
+
motion.mp4 (H.264, yuv420p, faststart; --webm adds VP9; --audio is muxed and fitted to the
|
|
1405
|
+
length) and checks it: safe zone (text and controls inside the format's clear area, uncut)
|
|
1406
|
+
and overlaps (text over text), both by layout.js at the first frame, every 0.5 s and the
|
|
1407
|
+
last; loop seam (a loop's wrap frame against its first); blank frames (nearly one colour,
|
|
1408
|
+
outside the first and last 0.5 s); repeatable (--prove renders twice, every frame's bytes
|
|
1409
|
+
compared). motion.json holds the plan, every frame's hash and the checks; a project's
|
|
1410
|
+
design/status.json gets a motion part on the zero board (errors, overlaps, seams).
|
|
1411
|
+
Default out .funtools/motion/<scene>/ (never shipped). check judges a render folder again
|
|
1412
|
+
(or renders a scene without encoding); new writes <name>/index.html, a starter that passes
|
|
1413
|
+
exit 0 pass · 1 a check failed · 2 not observed (no browser) · 3 error
|
|
1414
|
+
compose HTML-composition video: a trailer, a 9:16 post or a playable ad's end card written as a
|
|
1415
|
+
HyperFrames composition (HTML, timed clips on tracks, GSAP titles, <audio>) with the game itself
|
|
1416
|
+
as a live clip (<iframe data-fpx-game="game/index.html">) that the composition's clock drives on the
|
|
1417
|
+
held clock (inputs on their frames; a game the clock cannot hold is filmed), rendered locally and
|
|
1418
|
+
deterministically by HyperFrames (Apache-2.0, run at its pinned version: headless Chrome seeks each
|
|
1419
|
+
frame, FFmpeg encodes). new writes design/compose/<name>.html from the game's build, key art,
|
|
1420
|
+
captures and sound; check runs the contract, the tools, the game probe and HyperFrames' lint; render
|
|
1421
|
+
writes .funtools/compose/<name>.mp4, <name>.sheet.jpg (12 frames to look at) and <name>.json.
|
|
1422
|
+
variations makes ad cuts from one brief (hooks, each with the capture or view it opens on, calls to
|
|
1423
|
+
action, lengths, aspects, an end card, max): at most max of the cross product, every value in a cut
|
|
1424
|
+
before any pair repeats, each its own composition (design/compose/ads/<id>.html) rendered on one stage;
|
|
1425
|
+
.funtools/compose/ads/<id>/ (mp4, contact sheet, record), index.json and sheet.png (a labelled frame
|
|
1426
|
+
per cut: the person picks which to publish). An end card unlike the playable's own words is a warn
|
|
1427
|
+
line; --dry-run prints the plan and renders nothing.
|
|
1428
|
+
Needs Node 22+, ffmpeg and Playwright's headless Chromium (each missing one is named with its fix).
|
|
1429
|
+
exit 0 pass · 1 the composition or a check failed · 2 not observed · 3 error
|
|
1430
|
+
model the hosted Blender bench on a model a job of yours made, by asset id or by the
|
|
1431
|
+
file gen saved it as — nothing is uploaded; a file you changed since, or one no
|
|
1432
|
+
job made, is for --local. inspect measures it (free); render photographs it
|
|
1433
|
+
(--frames, --size: views round it, a front view, a contact sheet); finalize
|
|
1434
|
+
repairs broken materials, drops clips not in --keep-clips and renders the proof;
|
|
1435
|
+
skin has a vision pass mark the exposed skin and paints it one --tone (reuse the
|
|
1436
|
+
tone across a cast); retexture repaints the model's four sides with the new look
|
|
1437
|
+
(your game's style lock with --game-id) and projects them back onto its textures;
|
|
1438
|
+
level builds a spec (rooms, walls, ramps, props, markers; TOOLS_API.md) into a GLB
|
|
1439
|
+
with colliders and named markers, plus a plan and a corner view.
|
|
1440
|
+
Pictures go to .funtools/renders/<name>/ (never shipped). Exit as gen.
|
|
1441
|
+
--local: the bench's verbs on your own Blender 4.2+ (found in Applications,
|
|
1442
|
+
Program Files or PATH, or FUNPIXEL_BLENDER_BIN): free, nothing is uploaded.
|
|
1443
|
+
inspect describes it; normalize (--height-m) and decimate (--target-triangles,
|
|
1444
|
+
never welds) write <name>.<verb>.glb or --out; render writes its PNGs to
|
|
1445
|
+
.funtools/renders/<name>/ or --out; finalize writes <name>.finalize.glb and
|
|
1446
|
+
its proof pictures; level builds a spec on your Blender, free, into
|
|
1447
|
+
public/assets/models/<name>.glb and .funtools/renders/<name>/. The bench scripts ship with
|
|
1448
|
+
funtools under their own GPL-3.0-or-later licence (FUNPIXEL_BENCH_DIR overrides)
|
|
1449
|
+
exit 0 done · 1 Blender refused the model · 3 setup error
|
|
1450
|
+
clips: the animation clip names in a GLB/glTF, read locally (no Blender, free),
|
|
1451
|
+
and any name inside another (run / run_fast, idle / idle_long, walk / walkback),
|
|
1452
|
+
repeated or missing: a lookup by name can play the wrong clip
|
|
1453
|
+
exit 0 every name apart · 1 a collision · 2 no clips (not observed) · 3 error
|
|
1454
|
+
fidelity how closely a 3D model matches its reference views, 0-100 each (the owner's
|
|
1455
|
+
scorer): silhouette 35%, palette 30% (ΔE), tonal structure 20%, ink lines 15%, each view
|
|
1456
|
+
against its best of the bench's 48 fixed cameras (8 yaws × 3 heights × both ways up),
|
|
1457
|
+
rendered on your own Blender 4.2+ (free) at the references' own size, or --renders <dir>.
|
|
1458
|
+
--own-axes for a model built square to its axes (no PCA), --roll for a deep model squared
|
|
1459
|
+
onto its side, --outline for inked references, --quarter-turns for a figure standing
|
|
1460
|
+
taller than long. It also names references no one object can match (proportions, parts,
|
|
1461
|
+
and --same-scale sizes on one sheet). scores.json + sheets in .funtools/fidelity/<model>/
|
|
1462
|
+
exit 0 scored (all ≥ --min) · 1 a view under --min · 2 nothing scored · 3 error
|
|
1463
|
+
login sign in in a browser (device code) and keep a new API key in the OS credential
|
|
1464
|
+
store; --key stores a key you made in Settings › Developers (--key - reads stdin)
|
|
1465
|
+
--api <url> signs in to another API (a local stack) and remembers it for later runs
|
|
1466
|
+
--scope names the new key's scopes instead of the default set (every scope but
|
|
1467
|
+
community:read and community:write, which a key holds only when asked for), e.g.
|
|
1468
|
+
--scope community:read,community:write for a key that reads and acts in Community
|
|
1469
|
+
whoami the account and key funtools is using
|
|
1470
|
+
usage credits charged by tool and by day, this UTC month or the last --days;
|
|
1471
|
+
--this-key counts only the key funtools is using
|
|
1472
|
+
budget this key's monthly credit cap, what it spent this month, the spend alerts it
|
|
1473
|
+
crossed (50, 80, 100%) and what the wallet holds; the cap is set in
|
|
1474
|
+
Settings › Developers
|
|
1475
|
+
logout remove the stored key
|
|
1476
|
+
publish funpixel (default): upload the staged game as a draft you own (needs a fresh
|
|
1477
|
+
verify.json and an API key). itch / pages / folder: ship dist/ elsewhere — the
|
|
1478
|
+
command is shown, and runs only with --yes or your confirm; logged to
|
|
1479
|
+
.funtools/publish.jsonl
|
|
1480
|
+
store the read-only half of store publishing (Google Play, the App Store, Steam), offline: \`init\` writes
|
|
1481
|
+
store/answers.json from what the game says (funpixel.json's name, the GDD's pitch), every other answer an
|
|
1482
|
+
explicit TODO (ids, prices, the SDK list, ages: the owner's); one SDK list gives both stores' privacy
|
|
1483
|
+
answers. \`doctor\`: what this Mac has for each store (asc and its version, DO_NOT_TRACK, the Play service
|
|
1484
|
+
account key's path in .env — only that the file exists, never its contents —, steamcmd), each line with
|
|
1485
|
+
its next step; never signs in, never calls a network API. \`lint\`: the answers against each store's rules
|
|
1486
|
+
(lengths, other stores' names, art sizes read from the files, the privacy URL, the privacy forms against
|
|
1487
|
+
the SDKs). \`plan\`: the ordered steps, each Script / Console / You as the store-readiness references
|
|
1488
|
+
mark them, what is already true, and a plan hash (answers + art bytes + store); .funtools/store-plan.json.
|
|
1489
|
+
store push and store release are not built yet
|
|
1490
|
+
exit init 0 written · 2 already there; doctor/lint 0 clean · 1 a line failed; lint/plan 2 no answers · 3 error`;
|
|
1491
|
+
export function commandHelp(command) {
|
|
1492
|
+
const [synopsis, described] = USAGE.split('\n\n');
|
|
1493
|
+
const pick = (lines, starts, more) => {
|
|
1494
|
+
const out = [];
|
|
1495
|
+
let on = false;
|
|
1496
|
+
for (const l of lines) {
|
|
1497
|
+
if (starts(l))
|
|
1498
|
+
on = true;
|
|
1499
|
+
else if (!more(l))
|
|
1500
|
+
on = false;
|
|
1501
|
+
if (on)
|
|
1502
|
+
out.push(l);
|
|
1503
|
+
}
|
|
1504
|
+
return out;
|
|
1505
|
+
};
|
|
1506
|
+
const syn = pick(synopsis.split('\n').slice(1), (l) => l.startsWith(` funtools ${command} `) || l === ` funtools ${command}`, (l) => /^ {4,}\S/.test(l) && !l.startsWith(' funtools'));
|
|
1507
|
+
const desc = pick(described.split('\n'), (l) => new RegExp(`^ ${command} +\\S`).test(l), (l) => /^ {11,}\S/.test(l));
|
|
1508
|
+
return ['usage:', ...syn, '', ...desc].join('\n');
|
|
1509
|
+
}
|
|
1510
|
+
const message = (e) => (e instanceof Error ? e.message : String(e));
|
|
1511
|
+
export function parse(rest, spec) {
|
|
1512
|
+
const flags = {};
|
|
1513
|
+
const positionals = [];
|
|
1514
|
+
for (let i = 0; i < rest.length; i++) {
|
|
1515
|
+
const a = rest[i];
|
|
1516
|
+
if (!a.startsWith('--') || a === '-') {
|
|
1517
|
+
positionals.push(a);
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
if (!Object.hasOwn(spec, a))
|
|
1521
|
+
return `unknown flag: ${a}`;
|
|
1522
|
+
if (!spec[a]) {
|
|
1523
|
+
flags[a] = true;
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
const v = rest[i + 1];
|
|
1527
|
+
if (v === undefined || (v.startsWith('--') && v !== '-'))
|
|
1528
|
+
return `${a} needs a value`;
|
|
1529
|
+
flags[a] = v;
|
|
1530
|
+
i++;
|
|
1531
|
+
}
|
|
1532
|
+
return { positionals, flags };
|
|
1533
|
+
}
|
|
1534
|
+
const str = (v) => (typeof v === 'string' ? v : undefined);
|
|
1535
|
+
const frontDoorOf = (flags) => str(flags['--click']) ?? (flags['--front-door'] ? FRONT_DOOR : null);
|
|
1536
|
+
function heldOf(flags, u) {
|
|
1537
|
+
const raw = str(flags['--seed']);
|
|
1538
|
+
if (raw !== undefined && !/^-?\d+$/.test(raw)) {
|
|
1539
|
+
u.error(`--seed ${raw}`, '--seed is a whole number (window.__FPX_SEED__ and Math.random play from it)');
|
|
1540
|
+
return null;
|
|
1541
|
+
}
|
|
1542
|
+
if (raw !== undefined && flags['--free-running']) {
|
|
1543
|
+
u.error('--seed goes with the held clock', 'drop --free-running, or --seed');
|
|
1544
|
+
return null;
|
|
1545
|
+
}
|
|
1546
|
+
return { ...(raw !== undefined ? { seed: Number(raw) } : {}), ...(flags['--free-running'] ? { freeRunning: true } : {}) };
|
|
1547
|
+
}
|
|
1548
|
+
function orientationOf(flags, vp, u) {
|
|
1549
|
+
const o = str(flags['--orientation']);
|
|
1550
|
+
if (o === undefined || (vp === 'phone' && (o === 'portrait' || o === 'landscape')))
|
|
1551
|
+
return o;
|
|
1552
|
+
if (o !== 'portrait' && o !== 'landscape')
|
|
1553
|
+
u.error(`unknown orientation: ${o}`, '--orientation is portrait or landscape');
|
|
1554
|
+
else
|
|
1555
|
+
u.error(`--orientation ${o} turns the phone`, 'add --viewport phone');
|
|
1556
|
+
return null;
|
|
1557
|
+
}
|
|
1558
|
+
const GEN_COMMANDS = new Set(['gen', 'character', 'estimate']);
|
|
1559
|
+
const GEN_SPEC = { ...Object.fromEntries(Object.entries(GEN_FLAGS).map(([f, d]) => [f, d.value])), '--json': false, '--score': true };
|
|
1560
|
+
const LOOK_SPEC = { '--viewport': true, '--front-door': false, '--click': true, '--json': false };
|
|
1561
|
+
export const SPECS = {
|
|
1562
|
+
init: { '--framework': true, '--name': true, '--offline': false, '--idea': true, '--bare': false, '--no-git': false },
|
|
1563
|
+
dev: { '--no-devtools': false, '--relay': false, '--bots': true, '--no-hold': false, '--phone': false, '--host': true, '--open': false },
|
|
1564
|
+
open: {},
|
|
1565
|
+
serve: { '--port': true, '--host': true, '--no-compress': false },
|
|
1566
|
+
find: { '--json': false },
|
|
1567
|
+
mcp: {},
|
|
1568
|
+
setup: { '--host': true, '--dry-run': false, '--json': false },
|
|
1569
|
+
doctor: { '--add-hooks': false, '--host': false, '--json': false },
|
|
1570
|
+
upgrade: { '--self': false, '--discard': false },
|
|
1571
|
+
smoke: { '--json': false },
|
|
1572
|
+
'smoke-build': { '--plan': true, '--json': false },
|
|
1573
|
+
test: { '--node': false, '--goals': false, '--json': false },
|
|
1574
|
+
check: { '--viewport': true, '--phone': false, '--front-door': false, '--click': true, '--no-test': false, '--no-idle': false, '--world': false, '--no-world': false, '--json': false },
|
|
1575
|
+
sim: {
|
|
1576
|
+
'--script': true, '--timeout': true, '--json': false,
|
|
1577
|
+
'--device': true, '--runtime': true, '--no-boot': false, '--owner': true, '--force': false, '--dry-run': false,
|
|
1578
|
+
'--orientation': true, '--app': true, '--out': true, '--points': false, '--tool': true,
|
|
1579
|
+
},
|
|
1580
|
+
unity: {
|
|
1581
|
+
'--project': true, '--unity': true, '--log': true, '--no-quit': false, '--max': true, '--stale-minutes': true, '--max-wait': true,
|
|
1582
|
+
'--days': true, '--yes': false, '--json': false, '--disk': false, '--below': true, '--only': true, '--apply': false,
|
|
1583
|
+
'--target': true, '--path': true, '--seconds': true, '--tier': true, '--budget': true, '--player': true, '--player-args': true, '--strict': false, '--rebuild': false, '--no-build': false,
|
|
1584
|
+
'--keep-target': false, '--port': true, '--udid': true, '--device': true, '--keep-sim': false, '--visible': false, '--sound': false,
|
|
1585
|
+
'--all': false, '--dev': false, '--settle-ms': true, '--points': false, '--mouse': false, '--from': true, '--git': true, '--ref': true, '--absolute': false,
|
|
1586
|
+
'--tests': false, '--dry-run': false,
|
|
1587
|
+
'--since': true, '--count': true, '--scene': false, '--game': false, '--out': true, '--open': false, '--wait': true,
|
|
1588
|
+
'--name': true, '--at': true, '--depth': true, '--force': false,
|
|
1589
|
+
},
|
|
1590
|
+
spec: { '--spec': true, '--tier': true, '--dir': true, '--json': false },
|
|
1591
|
+
rooms: { '--script': true, '--timeout': true, '--by': true, '--dir': true, '--json': false },
|
|
1592
|
+
depth: { '--out': true, '--place': true, '--tension': true, '--progress': true, '--fill': false, '--json': false },
|
|
1593
|
+
reach: { '--level': true, '--json': false },
|
|
1594
|
+
world: { '--dir': true, '--seed': true, '--rooms': true, '--kind': true, '--from': true, '--variety': true, '--force': false, '--json': false },
|
|
1595
|
+
feel: { '--script': true, '--engine': true, '--journey': true, '--no-sweep': false, '--json': false },
|
|
1596
|
+
gdd: { '--sources': false, '--strict': false, '--json': false },
|
|
1597
|
+
greenlight: { '--dir': true, '--version': true, '--override': true, '--target': true, '--by': true, '--from': true, '--why': true, '--json': false },
|
|
1598
|
+
verify: {
|
|
1599
|
+
'--engine': true,
|
|
1600
|
+
'--players': true, '--checks': true, '--manifest': true, '--no-sweep': false, '--watch': false, '--json': false,
|
|
1601
|
+
'--report': false, '--viewport': true, '--front-door': false, '--click': true,
|
|
1602
|
+
},
|
|
1603
|
+
capture: { ...LOOK_SPEC, '--engine': true, '--view': true, '--all': false, '--verbs': true, '--hold': true, '--burst': true, '--out': true, '--orientation': true, '--safari': false, '--seed': true, '--free-running': false },
|
|
1604
|
+
'audit-screens': { ...LOOK_SPEC, '--engine': true, '--views': true, '--orientation': true },
|
|
1605
|
+
compare: { '--baseline': false, '--json': false, '--target': true, '--view': true, '--viewport': true, '--fit': true, '--verbs': true, '--hold': true, '--front-door': false, '--click': true, '--out': true, '--engine': true, '--seed': true, '--free-running': false },
|
|
1606
|
+
parts: { '--grid': true, '--boxes': true, '--names': true, '--key': true, '--pivot': true, '--pad': true, '--merge': true, '--webp': false, '--out': true, '--json': false, '--clean': false, '--keep-shadows': false },
|
|
1607
|
+
cut: { '--grid': true, '--boxes': true, '--names': true, '--pivot': true, '--pad': true, '--merge': true, '--webp': false, '--out': true, '--json': false, '--keep-shadows': false },
|
|
1608
|
+
scene: { ...LOOK_SPEC, '--engine': true, '--orientation': true, '--view': true, '--verbs': true, '--hold': true, '--motion': false },
|
|
1609
|
+
hook: { ...LOOK_SPEC, '--engine': true, '--orientation': true, '--seconds': true, '--verbs': true, '--idle': false },
|
|
1610
|
+
life: { ...LOOK_SPEC, '--engine': true, '--orientation': true, '--view': true, '--moment': true },
|
|
1611
|
+
walk: { ...LOOK_SPEC, '--engine': true, '--orientation': true, '--route': true, '--chain': true, '--seconds': true, '--no-film': false },
|
|
1612
|
+
score: { '--viewport': true, '--json': false, '--engine': true, '--target': true, '--view': true, '--hero': true, '--hero-share': true, '--capture': true, '--no-judge': false, '--fixes': false, '--seed': true, '--free-running': false },
|
|
1613
|
+
framing: { ...LOOK_SPEC, '--engine': true, '--actors': true, '--view': true },
|
|
1614
|
+
places: { ...LOOK_SPEC, '--engine': true, '--view': true },
|
|
1615
|
+
boxes: { ...LOOK_SPEC, '--engine': true, '--orientation': true, '--view': true },
|
|
1616
|
+
'compare-takes': { '--names': true, '--route': true, '--viewport': true, '--seconds': true, '--no-score': false, '--out': true, '--json': false, '--engine': true },
|
|
1617
|
+
profile: { ...LOOK_SPEC, '--views': true, '--seed': true, '--frames': true, '--settle': true, '--against': true, '--software': false, '--throttle': true, '--repeat': true, '--compare': false, '--budget': true },
|
|
1618
|
+
clip: { '--frames': true, '--fps': true, '--seats': true, '--dir': true, '--json': false },
|
|
1619
|
+
judge: { '--prompt': true, '--concept': true, '--medium': true, '--dir': true, '--json': false },
|
|
1620
|
+
phone: { '--sim': true, '--orientation': true, '--seconds': true, '--taps': true, '--swipes': true, '--keep': false, '--out': true, '--json': false },
|
|
1621
|
+
inspect: { ...LOOK_SPEC, '--orientation': true, '--at': true, '--name': true, '--tree': false, '--url': true, '--view': true, '--verbs': true, '--settle': true },
|
|
1622
|
+
look: { ...LOOK_SPEC, '--dir': true, '--read': false, '--hud': false, '--fit': false, '--samples': true, '--every': true, '--verbs': true, '--orientation': true, '--view': true, '--hold': true, '--player': true, '--threats': true, '--colours': true, '--sheet': false, '--3d': false, '--static': false, '--sprites': true, '--sizes': true, '--ground': true, '--out': true },
|
|
1623
|
+
play: { '--viewport': true, '--engine': true, '--orientation': true, '--rounds': true, '--long': true, '--dir': true, '--round': true, '--refile': false, '--look': false, '--json': false },
|
|
1624
|
+
journey: { '--viewport': true, '--engine': true, '--orientation': true, '--seed': true, '--seconds': true, '--repeat': true, '--against': true, '--force': false, '--dir': true, '--json': false },
|
|
1625
|
+
playtest: { '--quick': false, '--versus': true, '--no-build': false, '--long': true, '--phone': false, '--sim': true, '--dir': true, '--cost-usd': true, '--hosted': false, '--yes': false, '--min-score': true, '--drop': true, '--by': true, '--json': false },
|
|
1626
|
+
'mobile-qa': { '--phone': false, '--sim': true, '--no-build': false, '--dir': true, '--json': false },
|
|
1627
|
+
flow: { '--idea': true, '--budget': true, '--autonomy': true, '--rounds': true, '--by': true, '--framework': true, '--name': true, '--bare': false, '--offline': false, '--no-git': false, '--dir': true, '--json': false },
|
|
1628
|
+
note: { '--stage': true, '--screenshot': true, '--by': true, '--evidence': true, '--force': false, '--hold': false, '--dir': true, '--json': false },
|
|
1629
|
+
owner: { '--repo': true, '--dir': true, '--json': false },
|
|
1630
|
+
firstlook: { '--by': true, '--dir': true, '--no-browser': false, '--page': true, '--json': false },
|
|
1631
|
+
progress: {
|
|
1632
|
+
'--stage': true, '--shot': true, '--clip': true, '--line': true, '--next': true, '--spent': true, '--evidence': true, '--decision': true,
|
|
1633
|
+
'--out': true, '--standin': true, '--tried': true, '--kind': true, '--options': true, '--default': true, '--timeout': true, '--for': true,
|
|
1634
|
+
'--dir': true, '--json': false,
|
|
1635
|
+
},
|
|
1636
|
+
feedback: {
|
|
1637
|
+
'--out': true, '--json': false, '--kind': true, '--cost-usd': true, '--minutes': true, '--evidence': true, '--dir': true,
|
|
1638
|
+
'--metric': true, '--before': true, '--after': true, '--unit': true, '--sha': true, '--film': true, '--test': true, '--minutes-lost': true,
|
|
1639
|
+
'--tool': true, '--stage': true, '--verdict': true, '--screenshot': true, '--propose': false, '--html': true,
|
|
1640
|
+
'--what': true, '--routes': true, '--overall': true, '--axes': true, '--winner': true, '--judges': true,
|
|
1641
|
+
},
|
|
1642
|
+
journal: { '--why': true, '--by': true, '--files': true, '--values': true, '--dir': true, '--json': false },
|
|
1643
|
+
decide: { '--because': true, '--over': true, '--supersedes': true, '--from': true, '--touches': true, '--dir': true, '--json': false },
|
|
1644
|
+
lanes: { '--stage': true, '--lanes': true, '--cap': true, '--timebox': true, '--by': true, '--line': true, '--shot': true, '--cost': true, '--minutes': true, '--final': false, '--all': false, '--dir': true, '--json': false },
|
|
1645
|
+
pick: { '--lane': true, '--question': true, '--by': true, '--dir': true, '--json': false },
|
|
1646
|
+
lessons: { '--fix': true, '--source': true, '--delta': true, '--genre': true, '--share': false, '--dir': true, '--json': false },
|
|
1647
|
+
pack: { '--dry-run': false, '--no-build': false, '--json': false, '--out': true },
|
|
1648
|
+
export: { '--dir': true, '--check': false, '--no-browser': false, '--strip-metadata': false, '--json': false },
|
|
1649
|
+
gen: { ...GEN_SPEC, '--out': true, '--unity': true, '--no-wait': false, '--yes': false, '--strip-title': false, '--cast': true },
|
|
1650
|
+
character: { ...GEN_SPEC, '--out': true, '--no-wait': false, '--yes': false },
|
|
1651
|
+
estimate: GEN_SPEC,
|
|
1652
|
+
assets: { '--only': true, '--family': true, '--stale': false, '--max-credits': true, '--batch': true, '--dry-run': false, '--yes': false, '--because': true, '--snap': false, '--write': false, '--from': true, '--by': true, '--dir': true, '--json': false },
|
|
1653
|
+
cast: { '--master': true, '--turnaround': true, '--pins': true, '--name': true, '--kind': true, '--features': true, '--may-change': true, '--moves': false, '--by': true, '--mode': true, '--dir': true, '--json': false },
|
|
1654
|
+
variety: { '--within': false, '--json': false },
|
|
1655
|
+
wow: { '--genre': true, '--json': false, '--signature': false, '--expression': true, '--why': true, '--check': true, '--dir': true },
|
|
1656
|
+
pitch: { '--why': true, '--label': true, '--picture': true, '--cost': true, '--note': true, '--dir': true, '--json': false, '--seed': true, '--dial': true, '--brief': true, '--text': true },
|
|
1657
|
+
loudness: { '--dir': true, '--json': false },
|
|
1658
|
+
zero: { '--json': false },
|
|
1659
|
+
review: { '--dir': true, '--json': false, '--loop': false, '--still': false, '--freeze': true, '--triangles': true, '--size': true, '--alpha': false, '--lufs': true },
|
|
1660
|
+
motion: {
|
|
1661
|
+
'--fps': true, '--seconds': true, '--frames': true, '--size': true, '--aspect': true, '--out': true, '--webm': false,
|
|
1662
|
+
'--audio': true, '--prove': false, '--seed': true, '--dir': true, '--json': false,
|
|
1663
|
+
},
|
|
1664
|
+
compose: {
|
|
1665
|
+
'--dir': true, '--name': true, '--aspect': true, '--seconds': true, '--fps': true, '--quality': true, '--software': false,
|
|
1666
|
+
'--force': false, '--out': true, '--max': true, '--dry-run': false, '--json': false,
|
|
1667
|
+
},
|
|
1668
|
+
beats: { '--dir': true, '--meter': true, '--json': false },
|
|
1669
|
+
schema: { '--strict': false, '--json': false },
|
|
1670
|
+
loop: { '--fps': true, '--min': true, '--max': true, '--size': true, '--key': true, '--name': true, '--out': true, '--dir': true, '--json': false },
|
|
1671
|
+
directions: { '--dirs': true, '--size': true, '--key': true, '--reverse': false, '--name': true, '--out': true, '--dir': true, '--json': false },
|
|
1672
|
+
board: { '--keep': true, '--lends': true, '--note': true, '--licence': true, '--by': true, '--title': true, '--limit': true, '--any-licence': false, '--dir': true, '--json': false },
|
|
1673
|
+
job: { '--wait': false, '--out': true, '--json': false },
|
|
1674
|
+
jobs: { '--tool': true, '--status': true, '--limit': true, '--cursor': true, '--refusals': false, '--json': false },
|
|
1675
|
+
optimize: {
|
|
1676
|
+
'--check': false, '--json': false, '--write': false, '--dir': true, '--max-size': true, '--format': true,
|
|
1677
|
+
'--quantize': false, '--padding': true, '--pot': false, '--no-trim': false, '--frame-size': true,
|
|
1678
|
+
'--out': true, '--out-dir': true, '--levels': true, '--class': true,
|
|
1679
|
+
},
|
|
1680
|
+
playable: { '--network': true, '--no-play': false, '--json': false },
|
|
1681
|
+
reel: { '--seconds': true, '--fps': true, '--viewport': true, '--frames-only': false, '--out': true, '--json': false },
|
|
1682
|
+
trailer: {
|
|
1683
|
+
'--seconds': true, '--fps': true, '--viewport': true, '--formats': true, '--for': true, '--music': true, '--no-music': false, '--no-sfx': false,
|
|
1684
|
+
'--title': true, '--key-art': true, '--logo': true, '--cta': true, '--intro': true, '--outro': true, '--out': true, '--json': false,
|
|
1685
|
+
},
|
|
1686
|
+
fidelity: {
|
|
1687
|
+
'--renders': true, '--out': true, '--size': true, '--own-axes': false, '--roll': false, '--outline': false,
|
|
1688
|
+
'--quarter-turns': false, '--same-scale': false, '--min': true, '--json': false,
|
|
1689
|
+
},
|
|
1690
|
+
model: {
|
|
1691
|
+
'--local': false, '--out': true, '--target-triangles': true, '--height-m': true, '--frames': true, '--size': true,
|
|
1692
|
+
'--keep-clips': true, '--tone': true, '--palette': true, '--game-id': true, '--quality': true,
|
|
1693
|
+
'--roughness': false, '--normal': false, '--json': false,
|
|
1694
|
+
},
|
|
1695
|
+
login: { '--key': true, '--api': true, '--scope': true }, logout: {}, whoami: {},
|
|
1696
|
+
usage: { '--days': true, '--this-key': false, '--json': false }, budget: { '--json': false },
|
|
1697
|
+
publish: {
|
|
1698
|
+
'--target': true, '--yes': false, '--name': true, '--game-id': true, '--json': false,
|
|
1699
|
+
'--update': false,
|
|
1700
|
+
'--itch': true, '--project': true, '--out': true,
|
|
1701
|
+
},
|
|
1702
|
+
store: { '--store': true, '--dir': true, '--json': false },
|
|
1703
|
+
models: { '--force': false, '--json': false },
|
|
1704
|
+
};
|
|
1705
|
+
export const ALIASES = { status: ['flow', 'status'] };
|
|
1706
|
+
export const VERSION_WORDS = ['--version', '-v', 'version'];
|
|
1707
|
+
export async function main(argv, io = console) {
|
|
1708
|
+
if (argv[0] !== undefined && Object.hasOwn(ALIASES, argv[0]))
|
|
1709
|
+
return main([...ALIASES[argv[0]], ...argv.slice(1)], io);
|
|
1710
|
+
const [command, ...rest] = argv;
|
|
1711
|
+
if (!command || command === '--help' || command === '-h') {
|
|
1712
|
+
io.log(USAGE);
|
|
1713
|
+
return command ? 0 : EXIT.error;
|
|
1714
|
+
}
|
|
1715
|
+
if (VERSION_WORDS.includes(command)) {
|
|
1716
|
+
io.log(versionLine());
|
|
1717
|
+
return 0;
|
|
1718
|
+
}
|
|
1719
|
+
const u = ui(io, command);
|
|
1720
|
+
const help = 'run `funtools --help` for the usage';
|
|
1721
|
+
if (!Object.hasOwn(SPECS, command)) {
|
|
1722
|
+
u.error('unknown command', help);
|
|
1723
|
+
return EXIT.error;
|
|
1724
|
+
}
|
|
1725
|
+
const dashes = command === 'unity' ? rest.indexOf('--') : -1;
|
|
1726
|
+
const passthrough = dashes >= 0 ? rest.slice(dashes + 1) : [];
|
|
1727
|
+
const own = dashes >= 0 ? rest.slice(0, dashes) : rest;
|
|
1728
|
+
if (own.includes('--help') || own.includes('-h')) {
|
|
1729
|
+
io.log(commandHelp(command));
|
|
1730
|
+
return 0;
|
|
1731
|
+
}
|
|
1732
|
+
const parsed = parse(own, SPECS[command]);
|
|
1733
|
+
if (typeof parsed === 'string') {
|
|
1734
|
+
u.error(parsed, help);
|
|
1735
|
+
return EXIT.error;
|
|
1736
|
+
}
|
|
1737
|
+
const { positionals, flags } = parsed;
|
|
1738
|
+
const target = positionals[0];
|
|
1739
|
+
const engineFlag = GEN_COMMANDS.has(command) ? undefined : str(flags['--engine']);
|
|
1740
|
+
if (engineFlag !== undefined) {
|
|
1741
|
+
if (!ENGINES.includes(engineFlag)) {
|
|
1742
|
+
u.error(`--engine ${engineFlag}: chromium or webkit`, help);
|
|
1743
|
+
return EXIT.error;
|
|
1744
|
+
}
|
|
1745
|
+
process.env.FUNTOOLS_ENGINE = engineFlag;
|
|
1746
|
+
}
|
|
1747
|
+
const json = !!flags['--json'];
|
|
1748
|
+
boardRun(command);
|
|
1749
|
+
try {
|
|
1750
|
+
switch (command) {
|
|
1751
|
+
case 'init': {
|
|
1752
|
+
const spoken = target !== undefined && /\s/.test(target.trim()) && !existsSync(target) && flags['--idea'] === undefined;
|
|
1753
|
+
if (positionals.length > (spoken ? 2 : 1)) {
|
|
1754
|
+
u.error('init takes one folder', 'funtools init <folder> --idea "one line", or funtools init "one line"');
|
|
1755
|
+
return EXIT.error;
|
|
1756
|
+
}
|
|
1757
|
+
const idea = spoken ? target : str(flags['--idea']);
|
|
1758
|
+
return await init({ target: spoken ? positionals[1] : target, framework: str(flags['--framework']), name: str(flags['--name']), offline: !!flags['--offline'], idea, bare: !!flags['--bare'], noGit: !!flags['--no-git'] }, io);
|
|
1759
|
+
}
|
|
1760
|
+
case 'dev': {
|
|
1761
|
+
const raw = str(flags['--bots']);
|
|
1762
|
+
const bots = raw === undefined ? undefined : parseBots(raw);
|
|
1763
|
+
if (typeof bots === 'string') {
|
|
1764
|
+
u.error(bots, help);
|
|
1765
|
+
return EXIT.error;
|
|
1766
|
+
}
|
|
1767
|
+
if (bots && !flags['--relay']) {
|
|
1768
|
+
u.error('--bots needs --relay', 'bots meet you in the local relay\'s dev room: funtools dev --relay --bots 3');
|
|
1769
|
+
return EXIT.error;
|
|
1770
|
+
}
|
|
1771
|
+
if (flags['--host'] && !flags['--phone']) {
|
|
1772
|
+
u.error('--host goes with --phone', 'funtools dev --phone [--host 0.0.0.0]');
|
|
1773
|
+
return EXIT.error;
|
|
1774
|
+
}
|
|
1775
|
+
if (flags['--no-hold'] && !flags['--relay']) {
|
|
1776
|
+
u.error('--no-hold goes with --relay', 'reloads are held only while a dev room is live');
|
|
1777
|
+
return EXIT.error;
|
|
1778
|
+
}
|
|
1779
|
+
return await dev({ target, devtools: !flags['--no-devtools'], relay: !!flags['--relay'], bots, phone: !!flags['--phone'], host: str(flags['--host']), holdReloads: !flags['--no-hold'], open: !!flags['--open'] }, io);
|
|
1780
|
+
}
|
|
1781
|
+
case 'serve': return await (await import("./commands/serve.js")).serve({ target, port: str(flags['--port']), host: str(flags['--host']), noCompress: !!flags['--no-compress'] }, io);
|
|
1782
|
+
case 'doctor': {
|
|
1783
|
+
if (!flags['--host'])
|
|
1784
|
+
return await doctor({ target, addHooks: !!flags['--add-hooks'] }, io);
|
|
1785
|
+
const named = target !== undefined && HOSTS.includes(target) && !existsSync(target);
|
|
1786
|
+
const other = !named && target !== undefined && /^[a-z]+$/.test(target) && !existsSync(target);
|
|
1787
|
+
return await doctorHost({ host: named || other ? target : undefined, target: named || other ? positionals[1] : target, json }, io);
|
|
1788
|
+
}
|
|
1789
|
+
case 'check': {
|
|
1790
|
+
const viewport = str(flags['--viewport']) ?? 'desktop';
|
|
1791
|
+
if (!VIEWPORT_CHOICES.includes(viewport)) {
|
|
1792
|
+
u.error(`unknown viewport: ${viewport}`, '--viewport is desktop, phone or both');
|
|
1793
|
+
return EXIT.error;
|
|
1794
|
+
}
|
|
1795
|
+
return await check({ target, viewport: viewport, frontDoor: frontDoorOf(flags), noTest: !!flags['--no-test'], noIdle: !!flags['--no-idle'], phone: !!flags['--phone'], ...(flags['--world'] ? { world: true } : flags['--no-world'] ? { world: false } : {}), json }, io);
|
|
1796
|
+
}
|
|
1797
|
+
case 'upgrade': return flags['--self'] ? await upgradeSelf(io) : flags['--discard'] ? await discardOffers({ target }, io) : await upgrade({ target }, io);
|
|
1798
|
+
case 'smoke': return await smoke({ target, json }, io);
|
|
1799
|
+
case 'smoke-build': {
|
|
1800
|
+
const plan = str(flags['--plan']);
|
|
1801
|
+
if (!plan) {
|
|
1802
|
+
u.error('smoke-build needs the plan', 'funtools smoke-build <fresh-folder> --plan <folder with design/plan.md and design/first-look/ref-*.png>');
|
|
1803
|
+
return EXIT.error;
|
|
1804
|
+
}
|
|
1805
|
+
const { smokeBuild } = await import("./commands/smoke-build.js");
|
|
1806
|
+
return await smokeBuild({ dir: positionals[0] ?? '.', plan, json, store: (await import("./keys/store.js")).osStore() }, io);
|
|
1807
|
+
}
|
|
1808
|
+
case 'compare': {
|
|
1809
|
+
const commas = (f) => (str(flags[f]) ?? '').split(',').map((v) => v.trim()).filter(Boolean);
|
|
1810
|
+
if (flags['--target'] === undefined) {
|
|
1811
|
+
const extra = ['--view', '--viewport', '--fit', '--verbs', '--hold', '--front-door', '--click', '--out', '--seed', '--free-running'].find((f) => flags[f] !== undefined);
|
|
1812
|
+
if (extra) {
|
|
1813
|
+
u.error(`${extra} goes with --target`, 'funtools compare --target design/first-look/walk.png --view playing');
|
|
1814
|
+
return EXIT.error;
|
|
1815
|
+
}
|
|
1816
|
+
return await compare({ target: target ?? '.', baseline: !!flags['--baseline'], json }, io);
|
|
1817
|
+
}
|
|
1818
|
+
if (flags['--baseline']) {
|
|
1819
|
+
u.error('--baseline and --target are two different compares', 'before | after: --baseline; today | target: --target');
|
|
1820
|
+
return EXIT.error;
|
|
1821
|
+
}
|
|
1822
|
+
const pairs = pairTargets(commas('--target'), commas('--view'));
|
|
1823
|
+
if (typeof pairs === 'string') {
|
|
1824
|
+
u.error(pairs, help);
|
|
1825
|
+
return EXIT.error;
|
|
1826
|
+
}
|
|
1827
|
+
const vp = str(flags['--viewport']) ?? 'both';
|
|
1828
|
+
if (!VIEWPORT_CHOICES.includes(vp)) {
|
|
1829
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is phone, desktop or both');
|
|
1830
|
+
return EXIT.error;
|
|
1831
|
+
}
|
|
1832
|
+
const fit = str(flags['--fit']) ?? 'contain';
|
|
1833
|
+
if (fit !== 'contain' && fit !== 'cover') {
|
|
1834
|
+
u.error(`--fit ${fit}`, '--fit is contain (the whole target) or cover (fill and crop)');
|
|
1835
|
+
return EXIT.error;
|
|
1836
|
+
}
|
|
1837
|
+
const hold = str(flags['--hold']) === undefined ? undefined : Number(str(flags['--hold']));
|
|
1838
|
+
if (hold !== undefined && (!Number.isFinite(hold) || hold < 0 || hold > 10_000)) {
|
|
1839
|
+
u.error('--hold is milliseconds from 0 to 10000', help);
|
|
1840
|
+
return EXIT.error;
|
|
1841
|
+
}
|
|
1842
|
+
const viewports = vp === 'both' ? ['phone', 'desktop'] : [vp];
|
|
1843
|
+
const framed = heldOf(flags, u);
|
|
1844
|
+
if (framed === null)
|
|
1845
|
+
return EXIT.error;
|
|
1846
|
+
return await compareTarget({ target: target ?? '.', pairs, viewports: [...viewports], fit, out: str(flags['--out']), click: frontDoorOf(flags), verbs: commas('--verbs'), holdMs: hold, json, ...framed }, io);
|
|
1847
|
+
}
|
|
1848
|
+
case 'parts':
|
|
1849
|
+
case 'cut': {
|
|
1850
|
+
if (!target) {
|
|
1851
|
+
u.error(`${command} needs a sheet`, command === 'cut' ? 'funtools cut design/first-look/fauna.jpg --names fox,jelly,crab,goat,whale' : 'funtools parts design/gen/ren-sheet.png --names head,torso,arm-l,arm-r,leg-l,leg-r');
|
|
1852
|
+
return EXIT.error;
|
|
1853
|
+
}
|
|
1854
|
+
if (flags['--grid'] !== undefined && flags['--boxes'] !== undefined) {
|
|
1855
|
+
u.error('--grid or --boxes, not both', help);
|
|
1856
|
+
return EXIT.error;
|
|
1857
|
+
}
|
|
1858
|
+
const grid = str(flags['--grid']) === undefined ? undefined : parseGrid(str(flags['--grid']));
|
|
1859
|
+
if (grid === null) {
|
|
1860
|
+
u.error(`--grid ${str(flags['--grid'])}`, '--grid is columns x rows, e.g. --grid 4x2');
|
|
1861
|
+
return EXIT.error;
|
|
1862
|
+
}
|
|
1863
|
+
const key = parseKey(str(flags['--key']) ?? 'auto');
|
|
1864
|
+
if (!key) {
|
|
1865
|
+
u.error(`--key ${str(flags['--key'])}`, '--key is auto, none, paper or a colour like #ffffff');
|
|
1866
|
+
return EXIT.error;
|
|
1867
|
+
}
|
|
1868
|
+
const pivot = str(flags['--pivot']) ?? 'bottom';
|
|
1869
|
+
if (pivot !== 'bottom' && pivot !== 'top' && pivot !== 'center') {
|
|
1870
|
+
u.error(`--pivot ${pivot}`, '--pivot is bottom (poses), top (limbs) or center');
|
|
1871
|
+
return EXIT.error;
|
|
1872
|
+
}
|
|
1873
|
+
const int = (f, d, hi) => {
|
|
1874
|
+
const raw = str(flags[f]);
|
|
1875
|
+
if (raw === undefined)
|
|
1876
|
+
return d;
|
|
1877
|
+
const n = Number(raw);
|
|
1878
|
+
if (Number.isInteger(n) && n >= 0 && n <= hi)
|
|
1879
|
+
return n;
|
|
1880
|
+
u.error(`${f} is a whole number of pixels from 0 to ${hi}`, help);
|
|
1881
|
+
return null;
|
|
1882
|
+
};
|
|
1883
|
+
const pad = int('--pad', 2, 64), merge = int('--merge', 6, 256);
|
|
1884
|
+
if (pad === null || merge === null)
|
|
1885
|
+
return EXIT.error;
|
|
1886
|
+
const names = (str(flags['--names']) ?? '').split(',').map((v) => v.trim()).filter(Boolean);
|
|
1887
|
+
if (command === 'cut')
|
|
1888
|
+
return await parts(cutDefaults({ sheet: target, grid, boxes: str(flags['--boxes']), names, out: str(flags['--out']), pivot, pad, merge, webp: !!flags['--webp'], json, keepShadows: !!flags['--keep-shadows'] }), io);
|
|
1889
|
+
return await parts({ sheet: target, grid, boxes: str(flags['--boxes']), names, out: str(flags['--out']), key, pivot, pad, merge, webp: !!flags['--webp'], json, clean: !!flags['--clean'], keepShadows: !!flags['--keep-shadows'] }, io);
|
|
1890
|
+
}
|
|
1891
|
+
case 'test': return await test({ target, node: !!flags['--node'], goals: !!flags['--goals'], json }, io);
|
|
1892
|
+
case 'sim': {
|
|
1893
|
+
if (isSimVerb(positionals[0])) {
|
|
1894
|
+
const verb = positionals[0];
|
|
1895
|
+
const owner = str(flags['--owner']);
|
|
1896
|
+
if (owner !== undefined && !/^\d+$/.test(owner)) {
|
|
1897
|
+
u.error(`--owner ${owner}`, 'the pid of the process that owns the lease');
|
|
1898
|
+
return EXIT.error;
|
|
1899
|
+
}
|
|
1900
|
+
const tool = str(flags['--tool']);
|
|
1901
|
+
if (tool !== undefined && tool !== 'baguette' && tool !== 'idb') {
|
|
1902
|
+
u.error(`--tool ${tool}`, 'baguette or idb');
|
|
1903
|
+
return EXIT.error;
|
|
1904
|
+
}
|
|
1905
|
+
return await simulator({
|
|
1906
|
+
verb, args: positionals.slice(1), device: str(flags['--device']), runtime: str(flags['--runtime']), boot: !flags['--no-boot'],
|
|
1907
|
+
owner: owner === undefined ? undefined : Number(owner), force: !!flags['--force'], dryRun: !!flags['--dry-run'],
|
|
1908
|
+
orientation: str(flags['--orientation']), app: str(flags['--app']), out: str(flags['--out']), points: !!flags['--points'], json,
|
|
1909
|
+
tool: tool,
|
|
1910
|
+
}, io);
|
|
1911
|
+
}
|
|
1912
|
+
const timeout = str(flags['--timeout']);
|
|
1913
|
+
if (timeout !== undefined && !(Number(timeout) > 0)) {
|
|
1914
|
+
u.error(`--timeout ${timeout}`, 'seconds before the probe is stopped, e.g. --timeout 600');
|
|
1915
|
+
return EXIT.error;
|
|
1916
|
+
}
|
|
1917
|
+
return await sim({ target, script: str(flags['--script']), timeout: timeout === undefined ? undefined : Number(timeout), json }, io);
|
|
1918
|
+
}
|
|
1919
|
+
case 'spec': {
|
|
1920
|
+
const known = SPEC_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
1921
|
+
const sub = (known ? positionals[0] : 'lint');
|
|
1922
|
+
const rest = known ? positionals.slice(1) : positionals;
|
|
1923
|
+
if (rest.length > 1) {
|
|
1924
|
+
u.error(`spec ${sub} takes one folder`, 'funtools spec lint [dir] | spec init [dir] --tier mid|deep');
|
|
1925
|
+
return EXIT.error;
|
|
1926
|
+
}
|
|
1927
|
+
return await spec({ sub, dir: str(flags['--dir']) ?? rest[0], spec: str(flags['--spec']), tier: str(flags['--tier']), json }, io);
|
|
1928
|
+
}
|
|
1929
|
+
case 'rooms': {
|
|
1930
|
+
const sub = (positionals[0] === 'open' || positionals[0] === 'plan' ? positionals[0] : 'run');
|
|
1931
|
+
const rest = sub === 'run' ? positionals : positionals.slice(1);
|
|
1932
|
+
const timeout = str(flags['--timeout']);
|
|
1933
|
+
if (timeout !== undefined && !(Number(timeout) > 0)) {
|
|
1934
|
+
u.error(`--timeout ${timeout}`, 'seconds before the probe is stopped, e.g. --timeout 600');
|
|
1935
|
+
return EXIT.error;
|
|
1936
|
+
}
|
|
1937
|
+
if (sub === 'open') {
|
|
1938
|
+
const [room, ...more] = rest;
|
|
1939
|
+
const isDir = (x) => !!x && existsSync(x) && !/\s/.test(x);
|
|
1940
|
+
const dir = str(flags['--dir']) ?? (isDir(more[0]) ? more[0] : undefined);
|
|
1941
|
+
const words = (isDir(more[0]) && !flags['--dir'] ? more.slice(1) : more).join(' ').trim() || undefined;
|
|
1942
|
+
return await roomsCommand({ sub, room, dir, by: str(flags['--by']), words, json }, io);
|
|
1943
|
+
}
|
|
1944
|
+
if (sub === 'plan')
|
|
1945
|
+
return await roomsCommand({ sub, dir: str(flags['--dir']) ?? rest[0], by: str(flags['--by']), json }, io);
|
|
1946
|
+
return await roomsCommand({ sub, dir: str(flags['--dir']) ?? rest[0], script: str(flags['--script']), timeout: timeout === undefined ? undefined : Number(timeout), json }, io);
|
|
1947
|
+
}
|
|
1948
|
+
case 'depth': return await depth({ target, out: str(flags['--out']), place: str(flags['--place']), tension: str(flags['--tension']), progress: str(flags['--progress']), fill: !!flags['--fill'], json }, io);
|
|
1949
|
+
case 'world': {
|
|
1950
|
+
const action = positionals[0];
|
|
1951
|
+
if (!WORLD_ACTIONS.includes(action ?? '')) {
|
|
1952
|
+
u.error(action ? `unknown world action: ${action}` : 'world needs an action', 'funtools world forge --seed 7, or funtools world check');
|
|
1953
|
+
return EXIT.error;
|
|
1954
|
+
}
|
|
1955
|
+
if (action === 'check') {
|
|
1956
|
+
const extra = ['--seed', '--rooms', '--kind', '--from', '--variety', '--force'].find((f) => flags[f] !== undefined);
|
|
1957
|
+
if (extra) {
|
|
1958
|
+
u.error(`${extra} goes with world forge`, 'world check [--dir game] [--json]');
|
|
1959
|
+
return EXIT.error;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
return await world({
|
|
1963
|
+
action: action, dir: str(flags['--dir']) ?? positionals[1], seed: str(flags['--seed']), rooms: str(flags['--rooms']),
|
|
1964
|
+
kind: str(flags['--kind']), from: str(flags['--from']), variety: str(flags['--variety']), force: !!flags['--force'], json,
|
|
1965
|
+
}, io);
|
|
1966
|
+
}
|
|
1967
|
+
case 'reach': return await reach({ target, levels: str(flags['--level'])?.split(',').map((l) => l.trim()).filter(Boolean), json }, io);
|
|
1968
|
+
case 'feel': return await feel({ target, script: str(flags['--script']), journey: str(flags['--journey']), sweep: !flags['--no-sweep'], json }, io);
|
|
1969
|
+
case 'greenlight': {
|
|
1970
|
+
const known = GREENLIGHT_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
1971
|
+
const sub = known ? positionals[0] : 'status';
|
|
1972
|
+
const choose = sub === 'worth' && positionals[1] === 'choose';
|
|
1973
|
+
const dir = str(flags['--dir']) ?? (choose ? undefined : known ? positionals[1] : positionals[0]) ?? '.';
|
|
1974
|
+
return await greenlight({ sub, dir, version: str(flags['--version']), override: str(flags['--override']), json, args: choose ? positionals.slice(1) : [], target: str(flags['--target']), by: str(flags['--by']), from: str(flags['--from']), why: str(flags['--why']) }, io);
|
|
1975
|
+
}
|
|
1976
|
+
case 'gdd': {
|
|
1977
|
+
const advice = { sources: !!flags['--sources'], strict: !!flags['--strict'] };
|
|
1978
|
+
return await gdd(positionals[0] === 'page' ? { sub: 'page', target: positionals[1], json } : positionals[0] === 'lint' ? { target: positionals[1], json, ...advice } : { target, json, ...advice }, io);
|
|
1979
|
+
}
|
|
1980
|
+
case 'export': return await exportCmd({ dest: target, dir: str(flags['--dir']), check: !!flags['--check'], browser: !flags['--no-browser'], stripMetadata: !!flags['--strip-metadata'], json }, io);
|
|
1981
|
+
case 'pack': return await pack({ target, build: !flags['--no-build'], json, out: str(flags['--out']) }, io);
|
|
1982
|
+
case 'gen': return await gen({ positionals, flags, json, out: str(flags['--out']), unity: str(flags['--unity']), wait: !flags['--no-wait'], yes: !!flags['--yes'], stripTitle: !!flags['--strip-title'], score: str(flags['--score']) }, io);
|
|
1983
|
+
case 'estimate': return await estimateCommand({ positionals, flags, json }, io);
|
|
1984
|
+
case 'assets': {
|
|
1985
|
+
const sub = positionals[0] ?? 'status';
|
|
1986
|
+
if (!ASSETS_SUBCOMMANDS.includes(sub)) {
|
|
1987
|
+
u.error(`unknown: assets ${sub}`, `use one of ${ASSETS_SUBCOMMANDS.join(', ')}`);
|
|
1988
|
+
return EXIT.error;
|
|
1989
|
+
}
|
|
1990
|
+
return await assets({
|
|
1991
|
+
sub, args: positionals.slice(1), dir: str(flags['--dir']), only: str(flags['--only']), family: str(flags['--family']),
|
|
1992
|
+
stale: !!flags['--stale'], maxCredits: str(flags['--max-credits']), dryRun: !!flags['--dry-run'], yes: !!flags['--yes'],
|
|
1993
|
+
because: str(flags['--because']), snap: !!flags['--snap'], batch: str(flags['--batch']), write: !!flags['--write'],
|
|
1994
|
+
from: str(flags['--from']), by: str(flags['--by']), json,
|
|
1995
|
+
}, io);
|
|
1996
|
+
}
|
|
1997
|
+
case 'cast': {
|
|
1998
|
+
const sub = positionals[0] ?? 'list';
|
|
1999
|
+
if (!CAST_SUBCOMMANDS.includes(sub)) {
|
|
2000
|
+
u.error(`unknown: cast ${sub}`, `use one of ${CAST_SUBCOMMANDS.join(', ')}`);
|
|
2001
|
+
return EXIT.error;
|
|
2002
|
+
}
|
|
2003
|
+
return await cast({
|
|
2004
|
+
sub, args: positionals.slice(1), dir: str(flags['--dir']), master: str(flags['--master']), turnaround: str(flags['--turnaround']),
|
|
2005
|
+
pins: str(flags['--pins']), name: str(flags['--name']), kind: str(flags['--kind']), features: str(flags['--features']),
|
|
2006
|
+
mayChange: str(flags['--may-change']), moves: !!flags['--moves'], by: str(flags['--by']), mode: str(flags['--mode']), json,
|
|
2007
|
+
}, io);
|
|
2008
|
+
}
|
|
2009
|
+
case 'character': return await character({ positionals, flags, json, out: str(flags['--out']), wait: !flags['--no-wait'], yes: !!flags['--yes'] }, io);
|
|
2010
|
+
case 'playable': return await playable({ target, network: str(flags['--network']), play: !flags['--no-play'], json }, io);
|
|
2011
|
+
case 'reel': {
|
|
2012
|
+
const num = (f, d, [lo, hi]) => {
|
|
2013
|
+
const v = str(flags[f]);
|
|
2014
|
+
const n = v === undefined ? d : Number(v);
|
|
2015
|
+
return Number.isFinite(n) && n >= lo && n <= hi ? n : `${f} is a number from ${lo} to ${hi}`;
|
|
2016
|
+
};
|
|
2017
|
+
const seconds = num('--seconds', 12, REEL_LIMITS.seconds), fps = num('--fps', 12, REEL_LIMITS.fps);
|
|
2018
|
+
if (typeof seconds === 'string' || typeof fps === 'string') {
|
|
2019
|
+
u.error(String(typeof seconds === 'string' ? seconds : fps), help);
|
|
2020
|
+
return EXIT.error;
|
|
2021
|
+
}
|
|
2022
|
+
const vp = str(flags['--viewport']);
|
|
2023
|
+
if (vp !== undefined && vp !== 'desktop' && vp !== 'phone') {
|
|
2024
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2025
|
+
return EXIT.error;
|
|
2026
|
+
}
|
|
2027
|
+
return await reel({ target, seconds, fps: Math.round(fps), viewport: vp, framesOnly: !!flags['--frames-only'], out: str(flags['--out']), json }, io);
|
|
2028
|
+
}
|
|
2029
|
+
case 'trailer': {
|
|
2030
|
+
const n = (f, d, [lo, hi]) => {
|
|
2031
|
+
const v = Number(str(flags[f]) ?? d);
|
|
2032
|
+
return Number.isFinite(v) && v >= lo && v <= hi ? Math.round(v) : null;
|
|
2033
|
+
};
|
|
2034
|
+
const seconds = n('--seconds', 30, TRAILER_LIMITS.seconds), fps = n('--fps', 15, TRAILER_LIMITS.fps);
|
|
2035
|
+
if (seconds === null) {
|
|
2036
|
+
u.error(`--seconds is a number from ${TRAILER_LIMITS.seconds.join(' to ')}`, help);
|
|
2037
|
+
return EXIT.error;
|
|
2038
|
+
}
|
|
2039
|
+
if (fps === null) {
|
|
2040
|
+
u.error(`--fps is a number from ${TRAILER_LIMITS.fps.join(' to ')}`, help);
|
|
2041
|
+
return EXIT.error;
|
|
2042
|
+
}
|
|
2043
|
+
const vp = str(flags['--viewport']);
|
|
2044
|
+
if (vp !== undefined && vp !== 'desktop' && vp !== 'phone') {
|
|
2045
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2046
|
+
return EXIT.error;
|
|
2047
|
+
}
|
|
2048
|
+
if (flags['--formats'] !== undefined && flags['--for'] !== undefined) {
|
|
2049
|
+
u.error('--formats or --for, not both', help);
|
|
2050
|
+
return EXIT.error;
|
|
2051
|
+
}
|
|
2052
|
+
const places = str(flags['--for']) === undefined ? null : formatsFor(str(flags['--for']).split(',').map((s) => s.trim()).filter(Boolean));
|
|
2053
|
+
if (typeof places === 'string') {
|
|
2054
|
+
u.error(places, help);
|
|
2055
|
+
return EXIT.error;
|
|
2056
|
+
}
|
|
2057
|
+
const formats = places ?? (str(flags['--formats']) ?? '16x9,9x16').split(',').map((s) => s.trim().replace(':', 'x')).filter(Boolean);
|
|
2058
|
+
if (!formats.length || formats.some((f) => !Object.hasOwn(SAFE, f))) {
|
|
2059
|
+
u.error(`--formats is any of ${Object.keys(SAFE).join(', ')}`, help);
|
|
2060
|
+
return EXIT.error;
|
|
2061
|
+
}
|
|
2062
|
+
if (flags['--music'] !== undefined && flags['--no-music']) {
|
|
2063
|
+
u.error('--music and --no-music together', help);
|
|
2064
|
+
return EXIT.error;
|
|
2065
|
+
}
|
|
2066
|
+
return await trailer({
|
|
2067
|
+
target, seconds, fps, viewport: vp, formats: [...new Set(formats)],
|
|
2068
|
+
music: str(flags['--music']), noMusic: !!flags['--no-music'], noSfx: !!flags['--no-sfx'], title: str(flags['--title']),
|
|
2069
|
+
keyArt: str(flags['--key-art']), logo: str(flags['--logo']), cta: str(flags['--cta']),
|
|
2070
|
+
intro: str(flags['--intro']), outro: str(flags['--outro']), out: str(flags['--out']), json,
|
|
2071
|
+
}, io);
|
|
2072
|
+
}
|
|
2073
|
+
case 'hook': {
|
|
2074
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2075
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2076
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2077
|
+
return EXIT.error;
|
|
2078
|
+
}
|
|
2079
|
+
const orientation = orientationOf(flags, vp, u);
|
|
2080
|
+
if (orientation === null)
|
|
2081
|
+
return EXIT.error;
|
|
2082
|
+
const seconds = Number(str(flags['--seconds']) ?? 180);
|
|
2083
|
+
if (!(seconds > 0 && seconds <= 900)) {
|
|
2084
|
+
u.error('--seconds must be between 1 and 900', help);
|
|
2085
|
+
return EXIT.error;
|
|
2086
|
+
}
|
|
2087
|
+
return await hook({ target, viewport: vp, orientation, json, click: frontDoorOf(flags) ?? FRONT_DOOR, seconds, idle: !!flags['--idle'], verbs: (str(flags['--verbs']) ?? '').split(',').map((v) => v.trim()).filter(Boolean) }, io);
|
|
2088
|
+
}
|
|
2089
|
+
case 'life': {
|
|
2090
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2091
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2092
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2093
|
+
return EXIT.error;
|
|
2094
|
+
}
|
|
2095
|
+
const orientation = orientationOf(flags, vp, u);
|
|
2096
|
+
if (orientation === null)
|
|
2097
|
+
return EXIT.error;
|
|
2098
|
+
return await life({ target, viewport: vp, orientation, json, click: frontDoorOf(flags) ?? FRONT_DOOR, view: str(flags['--view']) ?? null, moment: str(flags['--moment']) ?? null }, io);
|
|
2099
|
+
}
|
|
2100
|
+
case 'walk': {
|
|
2101
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2102
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2103
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2104
|
+
return EXIT.error;
|
|
2105
|
+
}
|
|
2106
|
+
const orientation = orientationOf(flags, vp, u);
|
|
2107
|
+
if (orientation === null)
|
|
2108
|
+
return EXIT.error;
|
|
2109
|
+
const secs = str(flags['--seconds']);
|
|
2110
|
+
const seconds = secs === undefined ? undefined : Number(secs);
|
|
2111
|
+
if (seconds !== undefined && !(seconds > 0 && seconds <= 900)) {
|
|
2112
|
+
u.error('--seconds must be between 1 and 900', help);
|
|
2113
|
+
return EXIT.error;
|
|
2114
|
+
}
|
|
2115
|
+
return await walk({ target, viewport: vp, orientation, json, click: null, routes: (str(flags['--route']) ?? '').split(',').map((v) => v.trim()).filter(Boolean), chain: (str(flags['--chain']) ?? '').split(',').map((v) => v.trim()).filter(Boolean), seconds, film: !flags['--no-film'] }, io);
|
|
2116
|
+
}
|
|
2117
|
+
case 'score': {
|
|
2118
|
+
if (flags['--fixes'])
|
|
2119
|
+
return printFixes(target ?? '.', json, io);
|
|
2120
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2121
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2122
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2123
|
+
return EXIT.error;
|
|
2124
|
+
}
|
|
2125
|
+
const pic = str(flags['--target']);
|
|
2126
|
+
const share = str(flags['--hero-share']);
|
|
2127
|
+
if (share !== undefined && !(Number(share) > 0 && Number(share) <= 1)) {
|
|
2128
|
+
u.error('--hero-share is the hero\'s height over the frame\'s, 0 to 1', help);
|
|
2129
|
+
return EXIT.error;
|
|
2130
|
+
}
|
|
2131
|
+
if (!pic && (flags['--view'] || flags['--hero'] || flags['--capture'] || share !== undefined)) {
|
|
2132
|
+
u.error('--view, --hero, --hero-share and --capture go with --target', 'funtools score --target design/first-look/walk.jpg --view walk');
|
|
2133
|
+
return EXIT.error;
|
|
2134
|
+
}
|
|
2135
|
+
const moments = pic ? [{ name: str(flags['--view']) ?? 'as-loaded', view: str(flags['--view']) ?? null, target: pic, hero: str(flags['--hero']) ?? null, heroShare: share === undefined ? null : Number(share), ...(str(flags['--capture']) ? { capture: str(flags['--capture']) } : {}) }] : [];
|
|
2136
|
+
const framed = heldOf(flags, u);
|
|
2137
|
+
if (framed === null)
|
|
2138
|
+
return EXIT.error;
|
|
2139
|
+
return await score({ target: target ?? '.', moments, viewport: vp, json, noJudge: !!flags['--no-judge'], ...framed }, io);
|
|
2140
|
+
}
|
|
2141
|
+
case 'compare-takes': {
|
|
2142
|
+
if (positionals.length !== 2) {
|
|
2143
|
+
u.error('compare-takes takes two game folders', 'funtools compare-takes builds/take-a builds/take-b [--names A,B]');
|
|
2144
|
+
return EXIT.error;
|
|
2145
|
+
}
|
|
2146
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2147
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2148
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2149
|
+
return EXIT.error;
|
|
2150
|
+
}
|
|
2151
|
+
const nm = (str(flags['--names']) ?? '').split(',').map((v) => v.trim()).filter(Boolean);
|
|
2152
|
+
if (nm.length && nm.length !== 2) {
|
|
2153
|
+
u.error('--names takes two names, A,B', help);
|
|
2154
|
+
return EXIT.error;
|
|
2155
|
+
}
|
|
2156
|
+
const secs = str(flags['--seconds']);
|
|
2157
|
+
const seconds = secs === undefined ? undefined : Number(secs);
|
|
2158
|
+
if (seconds !== undefined && !(seconds > 0 && seconds <= 900)) {
|
|
2159
|
+
u.error('--seconds must be between 1 and 900', help);
|
|
2160
|
+
return EXIT.error;
|
|
2161
|
+
}
|
|
2162
|
+
return await compareTakes({ takes: [positionals[0], positionals[1]], ...(nm.length ? { names: [nm[0], nm[1]] } : {}), routes: (str(flags['--route']) ?? '').split(',').map((v) => v.trim()).filter(Boolean), viewport: vp, seconds, score: !flags['--no-score'], out: str(flags['--out']), json }, io);
|
|
2163
|
+
}
|
|
2164
|
+
case 'boxes': {
|
|
2165
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2166
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2167
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2168
|
+
return EXIT.error;
|
|
2169
|
+
}
|
|
2170
|
+
const orientation = orientationOf(flags, vp, u);
|
|
2171
|
+
if (orientation === null)
|
|
2172
|
+
return EXIT.error;
|
|
2173
|
+
return await boxes({ target, viewport: vp, orientation, json, click: frontDoorOf(flags) ?? FRONT_DOOR, views: (str(flags['--view']) ?? '').split(',').map((v) => v.trim()).filter(Boolean) }, io);
|
|
2174
|
+
}
|
|
2175
|
+
case 'places': {
|
|
2176
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2177
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2178
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2179
|
+
return EXIT.error;
|
|
2180
|
+
}
|
|
2181
|
+
return await places({ target, viewport: vp, json, click: null, views: (str(flags['--view']) ?? '').split(',').map((v) => v.trim()).filter(Boolean) }, io);
|
|
2182
|
+
}
|
|
2183
|
+
case 'framing': {
|
|
2184
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2185
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2186
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2187
|
+
return EXIT.error;
|
|
2188
|
+
}
|
|
2189
|
+
return await framing({ target, viewport: vp, json, click: null, actors: (str(flags['--actors']) ?? '').split(',').map((v) => v.trim()).filter(Boolean), view: str(flags['--view']) ?? null }, io);
|
|
2190
|
+
}
|
|
2191
|
+
case 'capture':
|
|
2192
|
+
case 'audit-screens':
|
|
2193
|
+
case 'scene': {
|
|
2194
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2195
|
+
const turned = /^phone-(portrait|landscape)$/.exec(vp);
|
|
2196
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2197
|
+
u.error(`unknown viewport: ${vp}`, turned ? `--viewport phone --orientation ${turned[1]}` : '--viewport is desktop or phone');
|
|
2198
|
+
return EXIT.error;
|
|
2199
|
+
}
|
|
2200
|
+
const orientation = orientationOf(flags, vp, u);
|
|
2201
|
+
if (orientation === null)
|
|
2202
|
+
return EXIT.error;
|
|
2203
|
+
const hold = str(flags['--hold']) === undefined ? undefined : Number(str(flags['--hold']));
|
|
2204
|
+
if (hold !== undefined && (!Number.isFinite(hold) || hold < 0 || hold > 10_000)) {
|
|
2205
|
+
u.error('--hold is milliseconds from 0 to 10000', help);
|
|
2206
|
+
return EXIT.error;
|
|
2207
|
+
}
|
|
2208
|
+
const burst = str(flags['--burst']) === undefined ? undefined : Number(str(flags['--burst']));
|
|
2209
|
+
if (burst !== undefined && (!Number.isInteger(burst) || burst < 0 || burst > BURST.max)) {
|
|
2210
|
+
u.error(`--burst is a frame count from 0 to ${BURST.max}`, help);
|
|
2211
|
+
return EXIT.error;
|
|
2212
|
+
}
|
|
2213
|
+
const list = (f) => (str(flags[f]) ?? '').split(',').map((v) => v.trim()).filter(Boolean);
|
|
2214
|
+
const base = { target, viewport: vp, orientation, json, click: frontDoorOf(flags) };
|
|
2215
|
+
const framed = heldOf(flags, u);
|
|
2216
|
+
if (framed === null)
|
|
2217
|
+
return EXIT.error;
|
|
2218
|
+
if (command === 'audit-screens')
|
|
2219
|
+
return await auditScreens({ ...base, views: list('--views') }, io);
|
|
2220
|
+
if (flags['--safari'] && vp !== 'phone') {
|
|
2221
|
+
u.error('--safari is a phone in Mobile Safari', '--viewport phone --safari');
|
|
2222
|
+
return EXIT.error;
|
|
2223
|
+
}
|
|
2224
|
+
const look = { ...base, ...framed, safari: !!flags['--safari'], view: str(flags['--view']) ?? null, verbs: list('--verbs'), holdMs: hold, burst, out: str(flags['--out']), motion: !!flags['--motion'] };
|
|
2225
|
+
if (command === 'capture' && flags['--all']) {
|
|
2226
|
+
if (look.view !== null || look.out !== undefined || str(flags['--viewport']) !== undefined || orientation !== undefined) {
|
|
2227
|
+
u.error('--all shoots every view at both sizes: drop --view, --out, --viewport and --orientation', help);
|
|
2228
|
+
return EXIT.error;
|
|
2229
|
+
}
|
|
2230
|
+
return await captureAll({ target, json, click: base.click, verbs: look.verbs, holdMs: hold, burst, ...framed }, io);
|
|
2231
|
+
}
|
|
2232
|
+
return command === 'capture' ? await capture(look, io) : await scene(look, io);
|
|
2233
|
+
}
|
|
2234
|
+
case 'play': {
|
|
2235
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2236
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2237
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2238
|
+
return EXIT.error;
|
|
2239
|
+
}
|
|
2240
|
+
const rounds = Number(str(flags['--rounds']) ?? PLAY_DEFAULTS.rounds);
|
|
2241
|
+
if (!Number.isInteger(rounds) || rounds < 1 || rounds > 10) {
|
|
2242
|
+
u.error('--rounds is 1 to 10', help);
|
|
2243
|
+
return EXIT.error;
|
|
2244
|
+
}
|
|
2245
|
+
const sub = PLAY_SUBCOMMANDS.includes(positionals[0] ?? '') ? positionals[0] : 'open';
|
|
2246
|
+
const orientation = orientationOf(flags, vp, u);
|
|
2247
|
+
if (orientation === null)
|
|
2248
|
+
return EXIT.error;
|
|
2249
|
+
const long = str(flags['--long']) === undefined ? undefined : Number(str(flags['--long']));
|
|
2250
|
+
if (long !== undefined && !(Number.isInteger(long) && long >= 1 && long <= PLAY_STEP_LIMITS.maxLongMinutes)) {
|
|
2251
|
+
u.error(`--long is whole minutes of game time, 1 to ${PLAY_STEP_LIMITS.maxLongMinutes}`, help);
|
|
2252
|
+
return EXIT.error;
|
|
2253
|
+
}
|
|
2254
|
+
const roundRaw = str(flags['--round']);
|
|
2255
|
+
const round = roundRaw === undefined ? undefined : Number(roundRaw);
|
|
2256
|
+
if (round !== undefined && !(Number.isInteger(round) && round >= 1)) {
|
|
2257
|
+
u.error('--round is the round number play printed when it opened (1, 2, …)', help);
|
|
2258
|
+
return EXIT.error;
|
|
2259
|
+
}
|
|
2260
|
+
return await play({ sub, args: sub === 'open' ? positionals : positionals.slice(1), dir: str(flags['--dir']), round, refile: !!flags['--refile'], look: !!flags['--look'], viewport: vp, orientation, rounds, long, json }, io);
|
|
2261
|
+
}
|
|
2262
|
+
case 'journey': {
|
|
2263
|
+
const vp = str(flags['--viewport']);
|
|
2264
|
+
if (vp !== undefined && vp !== 'desktop' && vp !== 'phone') {
|
|
2265
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2266
|
+
return EXIT.error;
|
|
2267
|
+
}
|
|
2268
|
+
const orientation = orientationOf(flags, vp ?? 'desktop', u);
|
|
2269
|
+
if (orientation === null)
|
|
2270
|
+
return EXIT.error;
|
|
2271
|
+
const int = (f, lo, hi) => {
|
|
2272
|
+
const raw = str(flags[f]);
|
|
2273
|
+
if (raw === undefined)
|
|
2274
|
+
return undefined;
|
|
2275
|
+
const n = Number(raw);
|
|
2276
|
+
if (Number.isInteger(n) && n >= lo && n <= hi)
|
|
2277
|
+
return n;
|
|
2278
|
+
u.error(`${f} is a whole number from ${lo} to ${hi}`, help);
|
|
2279
|
+
return null;
|
|
2280
|
+
};
|
|
2281
|
+
const seed = int('--seed', -2147483648, 2147483647), seconds = int('--seconds', 5, 1800), repeat = int('--repeat', 1, 3), against = int('--against', 1, 1_000_000);
|
|
2282
|
+
if (seed === null || seconds === null || repeat === null || against === null)
|
|
2283
|
+
return EXIT.error;
|
|
2284
|
+
const sub = JOURNEY_SUBCOMMANDS.includes(positionals[0] ?? '') ? positionals[0] : 'list';
|
|
2285
|
+
if (sub === 'list' && positionals.length) {
|
|
2286
|
+
u.error(`unknown: journey ${positionals[0]}`, 'journey, journey record|from-play|replay|diff');
|
|
2287
|
+
return EXIT.error;
|
|
2288
|
+
}
|
|
2289
|
+
return await journey({ sub, args: positionals.slice(1), dir: str(flags['--dir']) ?? '.', viewport: vp, orientation, seed, seconds, repeat, against, force: !!flags['--force'], json }, io);
|
|
2290
|
+
}
|
|
2291
|
+
case 'playtest': {
|
|
2292
|
+
const sub = PLAYTEST_SUBCOMMANDS.includes(positionals[0] ?? '') ? positionals[0] : 'run';
|
|
2293
|
+
const cost = str(flags['--cost-usd']);
|
|
2294
|
+
if (cost !== undefined && !(Number(cost) >= 0)) {
|
|
2295
|
+
u.error('--cost-usd is a number of dollars', help);
|
|
2296
|
+
return EXIT.error;
|
|
2297
|
+
}
|
|
2298
|
+
const dir = str(flags['--dir']) ?? (sub === 'run' ? positionals[0] : undefined) ?? '.';
|
|
2299
|
+
const longRaw = str(flags['--long']);
|
|
2300
|
+
const long = longRaw === undefined ? undefined : Number(longRaw);
|
|
2301
|
+
if (long !== undefined && !(Number.isInteger(long) && long >= 0 && long <= PLAY_STEP_LIMITS.maxLongMinutes)) {
|
|
2302
|
+
u.error(`--long is whole minutes of game time for the blind round, 0 (a short round) to ${PLAY_STEP_LIMITS.maxLongMinutes}`, help);
|
|
2303
|
+
return EXIT.error;
|
|
2304
|
+
}
|
|
2305
|
+
const minRaw = str(flags['--min-score']);
|
|
2306
|
+
const minScore = minRaw === undefined ? undefined : Number(minRaw);
|
|
2307
|
+
if (minScore !== undefined && !(minScore >= 0 && minScore <= 10)) {
|
|
2308
|
+
u.error('--min-score is the panel\'s overall mean to require, 0 to 10', help);
|
|
2309
|
+
return EXIT.error;
|
|
2310
|
+
}
|
|
2311
|
+
if (minScore !== undefined && sub !== 'report') {
|
|
2312
|
+
u.error('--min-score goes with `playtest report`', 'the panel informs; only a report you ask to can fail on its number');
|
|
2313
|
+
return EXIT.error;
|
|
2314
|
+
}
|
|
2315
|
+
const common = { dir, quick: !!flags['--quick'], build: !flags['--no-build'], versus: str(flags['--versus']) ?? null, costUsd: cost === undefined ? null : Number(cost), long, phone: !!flags['--phone'], sim: str(flags['--sim']), json, minScore };
|
|
2316
|
+
if (flags['--hosted'])
|
|
2317
|
+
return await playtestHosted({ ...common, yes: !!flags['--yes'] }, io);
|
|
2318
|
+
if ((flags['--drop'] || flags['--by']) && sub !== 'keep') {
|
|
2319
|
+
u.error('--drop goes with `playtest keep`', 'playtest keep --drop K<n> --by owner "<their words>"');
|
|
2320
|
+
return EXIT.error;
|
|
2321
|
+
}
|
|
2322
|
+
return await playtest({ sub, ...common, ...(sub === 'keep' ? { drop: str(flags['--drop']), by: str(flags['--by']), words: positionals.slice(1).join(' ') } : {}) }, io);
|
|
2323
|
+
}
|
|
2324
|
+
case 'mobile-qa': {
|
|
2325
|
+
const sub = positionals[0] === 'report' ? 'report' : 'run';
|
|
2326
|
+
const dir = str(flags['--dir']) ?? (sub === 'run' ? positionals[0] : undefined) ?? '.';
|
|
2327
|
+
return await mobileQa({ sub, dir, build: !flags['--no-build'], phone: !!flags['--phone'], sim: str(flags['--sim']), json }, io);
|
|
2328
|
+
}
|
|
2329
|
+
case 'flow': {
|
|
2330
|
+
const known = FLOW_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2331
|
+
const sub = (known ? positionals[0] : 'status');
|
|
2332
|
+
const rest = known ? positionals.slice(1) : positionals;
|
|
2333
|
+
const isDir = (x) => !!x && existsSync(x) && !/\s/.test(x);
|
|
2334
|
+
const worded = sub === 'agree' || sub === 'drop';
|
|
2335
|
+
const dir = str(flags['--dir']) ?? (worded ? (isDir(rest[0]) ? rest[0] : undefined) : rest[0]) ?? '.';
|
|
2336
|
+
const args = worded ? rest.slice(isDir(rest[0]) && !flags['--dir'] ? 1 : 0) : [];
|
|
2337
|
+
return await flow({ sub, dir, args, idea: str(flags['--idea']), budget: str(flags['--budget']), autonomy: str(flags['--autonomy']), rounds: str(flags['--rounds']), by: str(flags['--by']), framework: str(flags['--framework']), name: str(flags['--name']), bare: !!flags['--bare'], offline: !!flags['--offline'], noGit: !!flags['--no-git'], json }, io);
|
|
2338
|
+
}
|
|
2339
|
+
case 'progress': {
|
|
2340
|
+
const known = PROGRESS_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2341
|
+
if (!known && positionals.length) {
|
|
2342
|
+
u.error(`unknown: progress ${positionals[0]}`, `funtools progress ${PROGRESS_SUBCOMMANDS.join('|')}`);
|
|
2343
|
+
return EXIT.error;
|
|
2344
|
+
}
|
|
2345
|
+
const sub = (known ? positionals[0] : 'status');
|
|
2346
|
+
const port = str(flags['--port']);
|
|
2347
|
+
return await progress({
|
|
2348
|
+
sub, args: positionals.slice(1), dir: str(flags['--dir']), stage: str(flags['--stage']), shot: str(flags['--shot']), clip: str(flags['--clip']),
|
|
2349
|
+
line: str(flags['--line']), next: str(flags['--next']), spent: str(flags['--spent']), evidence: str(flags['--evidence']), decision: str(flags['--decision']),
|
|
2350
|
+
out: str(flags['--out']), standin: str(flags['--standin']), tried: str(flags['--tried']), kind: str(flags['--kind']), options: str(flags['--options']),
|
|
2351
|
+
default: str(flags['--default']), timeout: str(flags['--timeout']), for: str(flags['--for']), ...(port ? { port: Number(port) } : {}), phone: !!flags['--phone'], background: !!flags['--background'], json,
|
|
2352
|
+
store: (await import("./keys/store.js")).osStore(),
|
|
2353
|
+
}, io);
|
|
2354
|
+
}
|
|
2355
|
+
case 'note': {
|
|
2356
|
+
const known = NOTE_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2357
|
+
const sub = known ? positionals[0] : positionals.length ? 'add' : 'list';
|
|
2358
|
+
return await note({ sub, args: known ? positionals.slice(1) : positionals, stage: str(flags['--stage']), screenshot: str(flags['--screenshot']), by: str(flags['--by']), evidence: str(flags['--evidence']), force: !!flags['--force'], hold: !!flags['--hold'], dir: str(flags['--dir']), json }, io);
|
|
2359
|
+
}
|
|
2360
|
+
case 'owner': {
|
|
2361
|
+
const known = OWNER_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2362
|
+
if (!known && positionals.length && !existsSync(positionals[0])) {
|
|
2363
|
+
u.error(`unknown: owner ${positionals[0]}`, `funtools owner ${OWNER_SUBCOMMANDS.join('|')}`);
|
|
2364
|
+
return EXIT.error;
|
|
2365
|
+
}
|
|
2366
|
+
return await owner({ sub: (known ? positionals[0] : 'list'), args: known ? positionals.slice(1) : positionals, dir: str(flags['--dir']), repo: str(flags['--repo']), json }, io);
|
|
2367
|
+
}
|
|
2368
|
+
case 'firstlook': {
|
|
2369
|
+
const known = FIRSTLOOK_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2370
|
+
return await firstlook({ sub: known ? positionals[0] : 'list', args: known ? positionals.slice(1) : positionals, dir: str(flags['--dir']), by: str(flags['--by']), noBrowser: !!flags['--no-browser'], page: str(flags['--page']), json }, io);
|
|
2371
|
+
}
|
|
2372
|
+
case 'feedback': return await feedback({
|
|
2373
|
+
sub: positionals[0], dirs: positionals.slice(1), out: str(flags['--out']), json,
|
|
2374
|
+
text: positionals.slice(1).join(' '), kind: str(flags['--kind']), costUsd: str(flags['--cost-usd']),
|
|
2375
|
+
minutes: str(flags['--minutes']), evidence: str(flags['--evidence']), dir: str(flags['--dir']),
|
|
2376
|
+
metric: str(flags['--metric']), before: str(flags['--before']), after: str(flags['--after']), unit: str(flags['--unit']),
|
|
2377
|
+
sha: str(flags['--sha']), film: str(flags['--film']), test: str(flags['--test']), minutesLost: str(flags['--minutes-lost']),
|
|
2378
|
+
tool: str(flags['--tool']), stage: str(flags['--stage']), verdict: str(flags['--verdict']), screenshot: str(flags['--screenshot']),
|
|
2379
|
+
propose: !!flags['--propose'], html: str(flags['--html']),
|
|
2380
|
+
what: str(flags['--what']), routes: str(flags['--routes']), overall: str(flags['--overall']), axes: str(flags['--axes']),
|
|
2381
|
+
winner: str(flags['--winner']), judges: str(flags['--judges']),
|
|
2382
|
+
}, io);
|
|
2383
|
+
case 'journal': return await journal({
|
|
2384
|
+
text: positionals.join(' '), why: str(flags['--why']), by: str(flags['--by']), files: str(flags['--files']),
|
|
2385
|
+
values: str(flags['--values']), dir: str(flags['--dir']), json,
|
|
2386
|
+
}, io);
|
|
2387
|
+
case 'decide': return await decide({
|
|
2388
|
+
text: positionals.join(' '), because: str(flags['--because']), over: str(flags['--over']),
|
|
2389
|
+
supersedes: str(flags['--supersedes']), from: str(flags['--from']), touches: str(flags['--touches']), dir: str(flags['--dir']), json,
|
|
2390
|
+
}, io);
|
|
2391
|
+
case 'lanes': {
|
|
2392
|
+
const known = LANES_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2393
|
+
if (!known && positionals.length) {
|
|
2394
|
+
u.error(`unknown: lanes ${positionals[0]}`, `funtools lanes ${LANES_SUBCOMMANDS.join('|')}`);
|
|
2395
|
+
return EXIT.error;
|
|
2396
|
+
}
|
|
2397
|
+
return await lanes({
|
|
2398
|
+
sub: (known ? positionals[0] : 'status'), args: positionals.slice(1), dir: str(flags['--dir']), stage: str(flags['--stage']), lanes: str(flags['--lanes']),
|
|
2399
|
+
cap: str(flags['--cap']), timebox: str(flags['--timebox']), by: str(flags['--by']), line: str(flags['--line']), shot: str(flags['--shot']), cost: str(flags['--cost']),
|
|
2400
|
+
minutes: str(flags['--minutes']), final: !!flags['--final'], all: !!flags['--all'], json, has: (c) => c in SPECS,
|
|
2401
|
+
}, io);
|
|
2402
|
+
}
|
|
2403
|
+
case 'pick': {
|
|
2404
|
+
const known = PICK_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2405
|
+
const sub = known ? positionals[0] : positionals.length ? 'propose' : 'list';
|
|
2406
|
+
return await pick({ sub, args: known ? positionals.slice(1) : positionals, lane: str(flags['--lane']), question: str(flags['--question']), by: str(flags['--by']), dir: str(flags['--dir']), json }, io);
|
|
2407
|
+
}
|
|
2408
|
+
case 'variety': return await variety({ target, json, within: !!flags['--within'] }, io);
|
|
2409
|
+
case 'wow': {
|
|
2410
|
+
const sub = positionals[0] === 'pick' || positionals[0] === 'check' ? positionals[0] : 'list';
|
|
2411
|
+
const dir = sub === 'pick' ? str(flags['--dir']) ?? '.' : (sub === 'check' ? positionals[1] : positionals[0]) ?? str(flags['--dir']) ?? '.';
|
|
2412
|
+
return await wow({ sub, dir, genre: str(flags['--genre']), id: sub === 'pick' ? positionals[1] : undefined, signature: !!flags['--signature'], expression: str(flags['--expression']), why: str(flags['--why']), check: str(flags['--check']), json }, io);
|
|
2413
|
+
}
|
|
2414
|
+
case 'pitch': {
|
|
2415
|
+
if (positionals[0] === 'widen') {
|
|
2416
|
+
const act = positionals[1] === 'score' || positionals[1] === 'keep' ? positionals[1] : 'draw';
|
|
2417
|
+
const after = positionals.slice(act === 'draw' ? 1 : 2);
|
|
2418
|
+
return await widen({
|
|
2419
|
+
action: act, card: act === 'keep' ? after[0] : undefined, dir: str(flags['--dir']) ?? (act === 'keep' ? after[1] : after[0]),
|
|
2420
|
+
seed: str(flags['--seed']), dial: str(flags['--dial']), brief: str(flags['--brief']), text: str(flags['--text']), json,
|
|
2421
|
+
}, io);
|
|
2422
|
+
}
|
|
2423
|
+
const known = PITCH_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2424
|
+
const sub = known ? positionals[0] : 'show';
|
|
2425
|
+
const rest = known ? positionals.slice(1) : positionals;
|
|
2426
|
+
const takes = sub === 'propose' || sub === 'pick';
|
|
2427
|
+
return await pitch({
|
|
2428
|
+
sub, dir: str(flags['--dir']) ?? (takes ? undefined : rest[0]), choice: takes ? rest[0] : undefined, option: takes ? rest[1] : undefined,
|
|
2429
|
+
why: str(flags['--why']), label: str(flags['--label']), picture: str(flags['--picture']), cost: str(flags['--cost']), note: str(flags['--note']), json,
|
|
2430
|
+
}, io);
|
|
2431
|
+
}
|
|
2432
|
+
case 'loudness': return await loudness({ paths: positionals, dir: str(flags['--dir']), json }, io);
|
|
2433
|
+
case 'zero': return await zero({ target, json }, io);
|
|
2434
|
+
case 'review': {
|
|
2435
|
+
const n = (f, ok) => {
|
|
2436
|
+
const v = str(flags[f]);
|
|
2437
|
+
if (v === undefined)
|
|
2438
|
+
return undefined;
|
|
2439
|
+
const x = Number(v);
|
|
2440
|
+
return Number.isFinite(x) && ok(x) ? x : null;
|
|
2441
|
+
};
|
|
2442
|
+
const freezeS = n('--freeze', (x) => x > 0 && x <= 600), triangles = n('--triangles', (x) => Number.isInteger(x) && x > 0), lufs = n('--lufs', (x) => x < 0 && x > -70);
|
|
2443
|
+
if (freezeS === null) {
|
|
2444
|
+
u.error('--freeze is seconds (more than 0)', help);
|
|
2445
|
+
return EXIT.error;
|
|
2446
|
+
}
|
|
2447
|
+
if (triangles === null) {
|
|
2448
|
+
u.error('--triangles is a whole number', help);
|
|
2449
|
+
return EXIT.error;
|
|
2450
|
+
}
|
|
2451
|
+
if (lufs === null) {
|
|
2452
|
+
u.error('--lufs is a loudness target below 0 (e.g. -14)', help);
|
|
2453
|
+
return EXIT.error;
|
|
2454
|
+
}
|
|
2455
|
+
const sz = str(flags['--size']);
|
|
2456
|
+
const m = sz === undefined ? null : /^(\d+)[x×](\d+)$/i.exec(sz);
|
|
2457
|
+
if (sz !== undefined && !m) {
|
|
2458
|
+
u.error('--size is WxH in pixels (e.g. 512x512)', help);
|
|
2459
|
+
return EXIT.error;
|
|
2460
|
+
}
|
|
2461
|
+
return await review({
|
|
2462
|
+
paths: positionals, dir: str(flags['--dir']), json, loop: !!flags['--loop'], still: !!flags['--still'], alpha: !!flags['--alpha'] || undefined,
|
|
2463
|
+
freezeS, triangles, lufs, ...(m ? { size: { w: Number(m[1]), h: Number(m[2]) } } : {}),
|
|
2464
|
+
}, io);
|
|
2465
|
+
}
|
|
2466
|
+
case 'motion': {
|
|
2467
|
+
const sub = MOTION_SUBCOMMANDS.includes(target ?? '') ? target : undefined;
|
|
2468
|
+
if (target !== undefined && !sub) {
|
|
2469
|
+
u.error(`motion ${target}: ${MOTION_SUBCOMMANDS.join(', ')}`, help);
|
|
2470
|
+
return EXIT.error;
|
|
2471
|
+
}
|
|
2472
|
+
return await motion({
|
|
2473
|
+
sub, args: positionals.slice(1), fps: str(flags['--fps']), seconds: str(flags['--seconds']), frames: str(flags['--frames']),
|
|
2474
|
+
size: str(flags['--size']), aspect: str(flags['--aspect']), out: str(flags['--out']), webm: !!flags['--webm'], audio: str(flags['--audio']),
|
|
2475
|
+
prove: !!flags['--prove'], seed: str(flags['--seed']), dir: str(flags['--dir']), json,
|
|
2476
|
+
}, io);
|
|
2477
|
+
}
|
|
2478
|
+
case 'compose': {
|
|
2479
|
+
const sub = COMPOSE_SUBCOMMANDS.includes(target ?? '') ? target : undefined;
|
|
2480
|
+
if (target !== undefined && !sub) {
|
|
2481
|
+
u.error(`compose ${target}: ${COMPOSE_SUBCOMMANDS.join(', ')}`, help);
|
|
2482
|
+
return EXIT.error;
|
|
2483
|
+
}
|
|
2484
|
+
return await compose({
|
|
2485
|
+
sub, args: positionals.slice(1), dir: str(flags['--dir']), name: str(flags['--name']), aspect: str(flags['--aspect']), seconds: str(flags['--seconds']),
|
|
2486
|
+
fps: str(flags['--fps']), quality: str(flags['--quality']), software: !!flags['--software'], force: !!flags['--force'], out: str(flags['--out']),
|
|
2487
|
+
max: str(flags['--max']), dryRun: !!flags['--dry-run'], json,
|
|
2488
|
+
}, io);
|
|
2489
|
+
}
|
|
2490
|
+
case 'beats': {
|
|
2491
|
+
const m = str(flags['--meter']);
|
|
2492
|
+
return await beats({ paths: positionals, dir: str(flags['--dir']), meter: m === undefined ? undefined : Number(m), json }, io);
|
|
2493
|
+
}
|
|
2494
|
+
case 'schema': return await schema({ target, strict: !!flags['--strict'], json }, io);
|
|
2495
|
+
case 'loop':
|
|
2496
|
+
case 'directions': {
|
|
2497
|
+
const num = (f) => {
|
|
2498
|
+
const v = str(flags[f]);
|
|
2499
|
+
if (v === undefined)
|
|
2500
|
+
return undefined;
|
|
2501
|
+
const n = Number(v);
|
|
2502
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
2503
|
+
};
|
|
2504
|
+
const bad = ['--fps', '--min', '--max', '--size', '--dirs'].find((f) => num(f) === null);
|
|
2505
|
+
if (bad) {
|
|
2506
|
+
u.error(`${bad} is a whole number`, help);
|
|
2507
|
+
return EXIT.error;
|
|
2508
|
+
}
|
|
2509
|
+
const size = num('--size');
|
|
2510
|
+
if (size !== undefined && (size < LOOP_LIMITS.size[0] || size > LOOP_LIMITS.size[1])) {
|
|
2511
|
+
u.error(`--size is ${LOOP_LIMITS.size.join(' to ')} pixels`, help);
|
|
2512
|
+
return EXIT.error;
|
|
2513
|
+
}
|
|
2514
|
+
if (command === 'directions')
|
|
2515
|
+
return await directions({ source: target, dirs: num('--dirs'), size, key: str(flags['--key']), reverse: !!flags['--reverse'], name: str(flags['--name']), out: str(flags['--out']), dir: str(flags['--dir']), json }, io);
|
|
2516
|
+
const fps = num('--fps'), min = num('--min'), max = num('--max');
|
|
2517
|
+
if (fps !== undefined && (fps < LOOP_LIMITS.fps[0] || fps > LOOP_LIMITS.fps[1])) {
|
|
2518
|
+
u.error(`--fps is ${LOOP_LIMITS.fps.join(' to ')}`, help);
|
|
2519
|
+
return EXIT.error;
|
|
2520
|
+
}
|
|
2521
|
+
if ([min, max].some((v) => v !== undefined && (v < LOOP_LIMITS.frames[0] || v > LOOP_LIMITS.frames[1]))) {
|
|
2522
|
+
u.error(`--min and --max are ${LOOP_LIMITS.frames.join(' to ')} frames`, help);
|
|
2523
|
+
return EXIT.error;
|
|
2524
|
+
}
|
|
2525
|
+
return await loop({ source: target, fps, min, max, size, key: str(flags['--key']), name: str(flags['--name']), out: str(flags['--out']), dir: str(flags['--dir']), json }, io);
|
|
2526
|
+
}
|
|
2527
|
+
case 'board': {
|
|
2528
|
+
const known = BOARD_SUBCOMMANDS.includes(positionals[0] ?? '');
|
|
2529
|
+
if (!known && positionals.length) {
|
|
2530
|
+
u.error(`unknown: board ${positionals[0]}`, `use one of ${BOARD_SUBCOMMANDS.join(', ')}`);
|
|
2531
|
+
return EXIT.error;
|
|
2532
|
+
}
|
|
2533
|
+
const sub = known ? positionals[0] : 'show';
|
|
2534
|
+
const lim = str(flags['--limit']);
|
|
2535
|
+
if (lim !== undefined && !(Number.isInteger(Number(lim)) && Number(lim) >= 1 && Number(lim) <= 20)) {
|
|
2536
|
+
u.error('--limit is 1 to 20', help);
|
|
2537
|
+
return EXIT.error;
|
|
2538
|
+
}
|
|
2539
|
+
return await board({
|
|
2540
|
+
sub, arg: positionals.slice(1).join(' ') || undefined, dir: str(flags['--dir']), limit: lim === undefined ? undefined : Number(lim),
|
|
2541
|
+
anyLicence: !!flags['--any-licence'], keep: str(flags['--keep']), lends: str(flags['--lends']), note: str(flags['--note']),
|
|
2542
|
+
licence: str(flags['--licence']), by: str(flags['--by']), title: str(flags['--title']), json,
|
|
2543
|
+
}, io);
|
|
2544
|
+
}
|
|
2545
|
+
case 'lessons': {
|
|
2546
|
+
const add = positionals[0] === 'add', motif = positionals[0] === 'motif';
|
|
2547
|
+
if (!add && !motif && positionals.length) {
|
|
2548
|
+
u.error(`unknown: lessons ${positionals[0]}`, 'funtools lessons, or lessons add "<finding>" --fix "<fix>"');
|
|
2549
|
+
return EXIT.error;
|
|
2550
|
+
}
|
|
2551
|
+
return await lessons({
|
|
2552
|
+
add, motif, finding: positionals.slice(1).join(' '), fix: str(flags['--fix']), source: str(flags['--source']), delta: str(flags['--delta']),
|
|
2553
|
+
genre: str(flags['--genre']), share: !!flags['--share'], dir: str(flags['--dir']), json,
|
|
2554
|
+
}, io);
|
|
2555
|
+
}
|
|
2556
|
+
case 'profile': {
|
|
2557
|
+
const vp = str(flags['--viewport']) ?? 'phone';
|
|
2558
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2559
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2560
|
+
return EXIT.error;
|
|
2561
|
+
}
|
|
2562
|
+
const int = (f, d, [lo, hi]) => {
|
|
2563
|
+
const n = Number(str(flags[f]) ?? d);
|
|
2564
|
+
return Number.isInteger(n) && n >= lo && n <= hi ? n : null;
|
|
2565
|
+
};
|
|
2566
|
+
const frames = int('--frames', 120, PROFILE_LIMITS.frames), settleMs = int('--settle', 1500, PROFILE_LIMITS.settleMs);
|
|
2567
|
+
if (frames === null) {
|
|
2568
|
+
u.error(`--frames is a whole number from ${PROFILE_LIMITS.frames.join(' to ')}`, help);
|
|
2569
|
+
return EXIT.error;
|
|
2570
|
+
}
|
|
2571
|
+
if (settleMs === null) {
|
|
2572
|
+
u.error(`--settle is milliseconds from ${PROFILE_LIMITS.settleMs.join(' to ')}`, help);
|
|
2573
|
+
return EXIT.error;
|
|
2574
|
+
}
|
|
2575
|
+
const seedRaw = str(flags['--seed']);
|
|
2576
|
+
const seed = seedRaw === undefined ? null : Number(seedRaw);
|
|
2577
|
+
if (seed !== null && !Number.isInteger(seed)) {
|
|
2578
|
+
u.error('--seed is a whole number', help);
|
|
2579
|
+
return EXIT.error;
|
|
2580
|
+
}
|
|
2581
|
+
const throttle = int('--throttle', 1, PROFILE_LIMITS.throttle);
|
|
2582
|
+
if (throttle === null) {
|
|
2583
|
+
u.error(`--throttle is a whole number from ${PROFILE_LIMITS.throttle.join(' to ')}`, help);
|
|
2584
|
+
return EXIT.error;
|
|
2585
|
+
}
|
|
2586
|
+
const repeat = int('--repeat', 1, PROFILE_LIMITS.repeat);
|
|
2587
|
+
if (repeat === null) {
|
|
2588
|
+
u.error(`--repeat is a whole number from ${PROFILE_LIMITS.repeat.join(' to ')}`, help);
|
|
2589
|
+
return EXIT.error;
|
|
2590
|
+
}
|
|
2591
|
+
const views = (str(flags['--views']) ?? '').split(',').map((v) => v.trim()).filter(Boolean);
|
|
2592
|
+
const budgetRaw = str(flags['--budget']);
|
|
2593
|
+
const budgetMs = budgetRaw === undefined ? undefined : Number(budgetRaw);
|
|
2594
|
+
if (budgetMs !== undefined && !(budgetMs >= PROFILE_LIMITS.budgetMs[0] && budgetMs <= PROFILE_LIMITS.budgetMs[1])) {
|
|
2595
|
+
u.error(`--budget is a frame budget in ms from ${PROFILE_LIMITS.budgetMs.join(' to ')}`, help);
|
|
2596
|
+
return EXIT.error;
|
|
2597
|
+
}
|
|
2598
|
+
if (budgetMs !== undefined && !flags['--compare']) {
|
|
2599
|
+
u.error('--budget is the burn rate\'s frame budget', 'add --compare');
|
|
2600
|
+
return EXIT.error;
|
|
2601
|
+
}
|
|
2602
|
+
return await profile({
|
|
2603
|
+
target, viewport: vp, views, seed, frames, settleMs, against: str(flags['--against']) ?? null, click: frontDoorOf(flags), json,
|
|
2604
|
+
software: !!flags['--software'], throttle, repeat, compare: !!flags['--compare'], budgetMs,
|
|
2605
|
+
}, io);
|
|
2606
|
+
}
|
|
2607
|
+
case 'clip': {
|
|
2608
|
+
const isReport = positionals[0] === 'report';
|
|
2609
|
+
const fpsRaw = str(flags['--fps']);
|
|
2610
|
+
const fps = fpsRaw === undefined ? undefined : Number(fpsRaw);
|
|
2611
|
+
if (fps !== undefined && !(fps >= 1 && fps <= 60)) {
|
|
2612
|
+
u.error('--fps is 1 to 60', help);
|
|
2613
|
+
return EXIT.error;
|
|
2614
|
+
}
|
|
2615
|
+
const seats = (str(flags['--seats']) ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
2616
|
+
return await clip({ sub: isReport ? 'report' : 'prepare', dir: str(flags['--dir']) ?? (isReport ? positionals[1] : positionals[0]) ?? '.', frames: str(flags['--frames']), fps, seats, json }, io);
|
|
2617
|
+
}
|
|
2618
|
+
case 'judge': {
|
|
2619
|
+
const sub = positionals[0];
|
|
2620
|
+
if (!JUDGE_SUBCOMMANDS.includes(sub)) {
|
|
2621
|
+
u.error(`judge ${sub ?? ''}: structure, identity or report`.replace(/ +:/, ':'), help);
|
|
2622
|
+
return EXIT.error;
|
|
2623
|
+
}
|
|
2624
|
+
const medium = str(flags['--medium']);
|
|
2625
|
+
if (medium !== undefined && medium !== 'model' && medium !== 'sprites') {
|
|
2626
|
+
u.error('--medium is model or sprites', help);
|
|
2627
|
+
return EXIT.error;
|
|
2628
|
+
}
|
|
2629
|
+
if (sub !== 'structure' && flags['--prompt'] !== undefined) {
|
|
2630
|
+
u.error('--prompt is for judge structure', help);
|
|
2631
|
+
return EXIT.error;
|
|
2632
|
+
}
|
|
2633
|
+
if (sub !== 'identity' && (flags['--concept'] !== undefined || medium)) {
|
|
2634
|
+
u.error('--concept and --medium are for judge identity', help);
|
|
2635
|
+
return EXIT.error;
|
|
2636
|
+
}
|
|
2637
|
+
return await judge({ sub, positionals: positionals.slice(1), dir: str(flags['--dir']) ?? '.', prompt: str(flags['--prompt']), concept: str(flags['--concept']), medium, json }, io);
|
|
2638
|
+
}
|
|
2639
|
+
case 'unity': {
|
|
2640
|
+
const num = (f) => { const v = str(flags[f]); return v === undefined ? undefined : Number(v); };
|
|
2641
|
+
for (const f of ['--max', '--stale-minutes', '--max-wait', '--days', '--below']) {
|
|
2642
|
+
const v = num(f);
|
|
2643
|
+
if (v !== undefined && !(v >= 0)) {
|
|
2644
|
+
u.error(`${f} ${str(flags[f])}`, 'a number');
|
|
2645
|
+
return 125;
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
const sub = positionals[0];
|
|
2649
|
+
if (sub !== 'run' && sub !== 'probe' && sub !== 'play' && sub !== 'capture' && passthrough.length) {
|
|
2650
|
+
u.error('arguments after -- go with unity run (Unity\'s) or probe, play and capture (the player\'s)', 'funtools unity run -- <unity args>');
|
|
2651
|
+
return 125;
|
|
2652
|
+
}
|
|
2653
|
+
if (sub === 'probe' || sub === 'play' || sub === 'capture') {
|
|
2654
|
+
for (const f of ['--seconds', '--port', '--settle-ms']) {
|
|
2655
|
+
const v = num(f);
|
|
2656
|
+
if (v !== undefined && !(v > 0)) {
|
|
2657
|
+
u.error(`${f} ${str(flags[f])}`, 'a number above 0');
|
|
2658
|
+
return 3;
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
const staleMinutes = num('--stale-minutes') ?? 20, maxRuns = num('--max') ?? 2, maxWait = num('--max-wait');
|
|
2662
|
+
return await unityPlayer({
|
|
2663
|
+
sub, args: positionals.slice(1), project: str(flags['--project']), target: str(flags['--target']), path: str(flags['--path']), seconds: num('--seconds'),
|
|
2664
|
+
tier: str(flags['--tier']), budget: str(flags['--budget']), player: str(flags['--player']), rebuild: !!flags['--rebuild'], noBuild: !!flags['--no-build'],
|
|
2665
|
+
keepTarget: !!flags['--keep-target'], port: num('--port'), udid: str(flags['--udid']), device: str(flags['--device']), keepSim: !!flags['--keep-sim'],
|
|
2666
|
+
visible: !!flags['--visible'], sound: !!flags['--sound'], out: str(flags['--out']), all: !!flags['--all'], dev: !!flags['--dev'], settleMs: num('--settle-ms'),
|
|
2667
|
+
playerArgs: passthrough, playerArgsFile: str(flags['--player-args']), strict: !!flags['--strict'],
|
|
2668
|
+
points: !!flags['--points'], mouse: !!flags['--mouse'], from: str(flags['--from']), git: str(flags['--git']), ref: str(flags['--ref']),
|
|
2669
|
+
absolute: !!flags['--absolute'], tests: !!flags['--tests'], force: !!flags['--force'], dryRun: !!flags['--dry-run'], json,
|
|
2670
|
+
queue: { staleMs: staleMinutes * 60_000, max: maxRuns, pollMs: 5000, maxWaitMs: maxWait === undefined ? null : maxWait * 60_000 },
|
|
2671
|
+
}, io);
|
|
2672
|
+
}
|
|
2673
|
+
if (sub === 'bridge')
|
|
2674
|
+
return await unityBridge({ action: positionals[1], project: str(flags['--project']), json }, io);
|
|
2675
|
+
if (sub === 'editor') {
|
|
2676
|
+
for (const f of ['--count', '--wait', '--depth']) {
|
|
2677
|
+
const v = num(f);
|
|
2678
|
+
if (v !== undefined && !(Number.isInteger(v) && v >= 1)) {
|
|
2679
|
+
u.error(`${f} ${str(flags[f])}`, 'a whole number, 1 or more');
|
|
2680
|
+
return 3;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
return await unityEditor({
|
|
2684
|
+
action: positionals[1], args: positionals.slice(2), project: str(flags['--project']), unity: str(flags['--unity']),
|
|
2685
|
+
since: str(flags['--since']), count: num('--count'), scene: !!flags['--scene'], game: !!flags['--game'], out: str(flags['--out']),
|
|
2686
|
+
open: !!flags['--open'], wait: num('--wait'), name: str(flags['--name']), at: str(flags['--at']), depth: num('--depth'),
|
|
2687
|
+
force: !!flags['--force'], json,
|
|
2688
|
+
}, io);
|
|
2689
|
+
}
|
|
2690
|
+
return await unity({
|
|
2691
|
+
sub, unityArgs: passthrough, project: str(flags['--project']), unity: str(flags['--unity']), log: str(flags['--log']), noQuit: !!flags['--no-quit'],
|
|
2692
|
+
max: num('--max'), staleMinutes: num('--stale-minutes'), maxWaitMinutes: num('--max-wait'),
|
|
2693
|
+
roots: sub === 'gc' ? positionals.slice(1) : [], days: num('--days'), yes: !!flags['--yes'], json,
|
|
2694
|
+
disk: !!flags['--disk'], below: num('--below'), only: str(flags['--only']), apply: !!flags['--apply'], strict: !!flags['--strict'],
|
|
2695
|
+
}, io);
|
|
2696
|
+
}
|
|
2697
|
+
case 'phone': {
|
|
2698
|
+
const o = str(flags['--orientation']);
|
|
2699
|
+
if (o !== undefined && o !== 'portrait' && o !== 'landscape') {
|
|
2700
|
+
u.error(`unknown orientation: ${o}`, '--orientation is portrait or landscape');
|
|
2701
|
+
return EXIT.error;
|
|
2702
|
+
}
|
|
2703
|
+
const seconds = Number(str(flags['--seconds']) ?? 6);
|
|
2704
|
+
if (!Number.isFinite(seconds) || seconds < 1 || seconds > 60) {
|
|
2705
|
+
u.error('--seconds is 1 to 60', help);
|
|
2706
|
+
return EXIT.error;
|
|
2707
|
+
}
|
|
2708
|
+
const taps = (str(flags['--taps']) ?? '').split(/\s+/).map((t) => t.trim()).filter(Boolean);
|
|
2709
|
+
const swipes = (str(flags['--swipes']) ?? '').split(/[\s,]+/).map((t) => t.trim()).filter(Boolean);
|
|
2710
|
+
return await phone({ target, sim: str(flags['--sim']), orientation: o, seconds, taps, swipes, keep: !!flags['--keep'], out: str(flags['--out']), json }, io);
|
|
2711
|
+
}
|
|
2712
|
+
case 'inspect': {
|
|
2713
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2714
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2715
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2716
|
+
return EXIT.error;
|
|
2717
|
+
}
|
|
2718
|
+
const orientation = orientationOf(flags, vp, u);
|
|
2719
|
+
if (orientation === null)
|
|
2720
|
+
return EXIT.error;
|
|
2721
|
+
const settle = Number(str(flags['--settle']) ?? 1500);
|
|
2722
|
+
if (!Number.isFinite(settle) || settle < 0 || settle > 20_000) {
|
|
2723
|
+
u.error('--settle is milliseconds from 0 to 20000', help);
|
|
2724
|
+
return EXIT.error;
|
|
2725
|
+
}
|
|
2726
|
+
const url = str(flags['--url']);
|
|
2727
|
+
if (url !== undefined && !/^https?:\/\//.test(url)) {
|
|
2728
|
+
u.error(`--url ${url} is not a web address`, 'the address `funtools dev` printed, e.g. http://localhost:5173');
|
|
2729
|
+
return EXIT.error;
|
|
2730
|
+
}
|
|
2731
|
+
return await inspect({
|
|
2732
|
+
target, url, at: str(flags['--at']), name: str(flags['--name']), tree: !!flags['--tree'], viewport: vp, orientation,
|
|
2733
|
+
view: str(flags['--view']) ?? null, verbs: (str(flags['--verbs']) ?? '').split(',').map((v) => v.trim()).filter(Boolean),
|
|
2734
|
+
settleMs: settle, click: frontDoorOf(flags), json,
|
|
2735
|
+
}, io);
|
|
2736
|
+
}
|
|
2737
|
+
case 'look': {
|
|
2738
|
+
if (THEME_SUBCOMMANDS.includes(positionals[0] ?? '')) {
|
|
2739
|
+
const sub = positionals[0];
|
|
2740
|
+
const dir = str(flags['--dir']) ?? (sub === 'report' ? positionals[1] : undefined) ?? '.';
|
|
2741
|
+
return await lookTheme({ sub, dir, pairs: sub === 'set' ? positionals.slice(1) : undefined, file: sub === 'patch' ? positionals[1] : undefined, json }, io);
|
|
2742
|
+
}
|
|
2743
|
+
if (flags['--hud']) {
|
|
2744
|
+
const vp = str(flags['--viewport']) ?? 'phone', orientation = str(flags['--orientation']);
|
|
2745
|
+
const samples = Number(str(flags['--samples']) ?? HUD_SAMPLING.samples), every = Number(str(flags['--every']) ?? HUD_SAMPLING.everyMs);
|
|
2746
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2747
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2748
|
+
return EXIT.error;
|
|
2749
|
+
}
|
|
2750
|
+
if (orientation !== undefined && orientation !== 'portrait' && orientation !== 'landscape') {
|
|
2751
|
+
u.error('--orientation is portrait or landscape', help);
|
|
2752
|
+
return EXIT.error;
|
|
2753
|
+
}
|
|
2754
|
+
if (!Number.isInteger(samples) || samples < 1 || samples > HUD_SAMPLING.max) {
|
|
2755
|
+
u.error(`--samples is a whole number from 1 to ${HUD_SAMPLING.max}`, help);
|
|
2756
|
+
return EXIT.error;
|
|
2757
|
+
}
|
|
2758
|
+
if (!Number.isFinite(every) || every < 100 || every > 5_000) {
|
|
2759
|
+
u.error('--every is milliseconds from 100 to 5000', help);
|
|
2760
|
+
return EXIT.error;
|
|
2761
|
+
}
|
|
2762
|
+
return await lookHud({ target, viewport: vp, orientation, view: str(flags['--view']) ?? null, verbs: (str(flags['--verbs']) ?? '').split(',').map((v) => v.trim()).filter(Boolean), samples, everyMs: every, click: frontDoorOf(flags), json }, io);
|
|
2763
|
+
}
|
|
2764
|
+
if (flags['--3d'])
|
|
2765
|
+
return await look3d({ target, static: !!flags['--static'], view: str(flags['--view']) ?? null, click: frontDoorOf(flags), json }, io);
|
|
2766
|
+
if (flags['--fit'])
|
|
2767
|
+
return await lookFit({ target, view: str(flags['--view']) ?? null, click: frontDoorOf(flags), json }, io);
|
|
2768
|
+
if (flags['--sheet'])
|
|
2769
|
+
return await lookSheet({ target, sprites: str(flags['--sprites']), sizes: str(flags['--sizes']), ground: str(flags['--ground']), out: str(flags['--out']), json }, io);
|
|
2770
|
+
if (!flags['--read']) {
|
|
2771
|
+
u.error('look measures --read, --hud, --fit, --sheet or --3d, or sets the look (set, patch, report)', 'funtools look --read [dir] · funtools look --hud [dir] · funtools look --fit [dir] · funtools look --sheet [dir] · funtools look --3d [dir] · funtools look set plate-ink=#3a2618 · funtools look report');
|
|
2772
|
+
return EXIT.error;
|
|
2773
|
+
}
|
|
2774
|
+
const vp = str(flags['--viewport']) ?? 'desktop';
|
|
2775
|
+
if (vp !== 'desktop' && vp !== 'phone') {
|
|
2776
|
+
u.error(`unknown viewport: ${vp}`, '--viewport is desktop or phone');
|
|
2777
|
+
return EXIT.error;
|
|
2778
|
+
}
|
|
2779
|
+
const hold = Number(str(flags['--hold']) ?? 1500);
|
|
2780
|
+
if (!Number.isFinite(hold) || hold < 0 || hold > 10_000) {
|
|
2781
|
+
u.error('--hold is milliseconds from 0 to 10000', help);
|
|
2782
|
+
return EXIT.error;
|
|
2783
|
+
}
|
|
2784
|
+
return await lookRead({
|
|
2785
|
+
target, viewport: vp, view: str(flags['--view']) ?? null, holdMs: hold, click: frontDoorOf(flags),
|
|
2786
|
+
player: str(flags['--player']), threats: str(flags['--threats']), colours: str(flags['--colours']), json,
|
|
2787
|
+
}, io);
|
|
2788
|
+
}
|
|
2789
|
+
case 'optimize': return await optimize({ positionals, flags, json }, io);
|
|
2790
|
+
case 'fidelity': return await fidelity({
|
|
2791
|
+
positionals, renders: str(flags['--renders']), out: str(flags['--out']), size: str(flags['--size']), min: str(flags['--min']),
|
|
2792
|
+
ownAxes: !!flags['--own-axes'], roll: !!flags['--roll'], outline: !!flags['--outline'],
|
|
2793
|
+
quarterTurns: !!flags['--quarter-turns'], sameScale: !!flags['--same-scale'], json,
|
|
2794
|
+
}, io);
|
|
2795
|
+
case 'model': return await model({
|
|
2796
|
+
local: !!flags['--local'], positionals, out: str(flags['--out']), json,
|
|
2797
|
+
targetTriangles: str(flags['--target-triangles']), heightM: str(flags['--height-m']),
|
|
2798
|
+
frames: str(flags['--frames']), size: str(flags['--size']), keepClips: str(flags['--keep-clips']),
|
|
2799
|
+
tone: str(flags['--tone']), palette: str(flags['--palette']), gameId: str(flags['--game-id']),
|
|
2800
|
+
quality: str(flags['--quality']), roughness: !!flags['--roughness'] || undefined, normal: !!flags['--normal'] || undefined,
|
|
2801
|
+
}, io);
|
|
2802
|
+
case 'job': return await job({ id: target, wait: !!flags['--wait'], out: str(flags['--out']), json }, io);
|
|
2803
|
+
case 'jobs': return await jobs({ tool: str(flags['--tool']), status: str(flags['--status']), limit: str(flags['--limit']), cursor: str(flags['--cursor']), refusals: !!flags['--refusals'], json }, io);
|
|
2804
|
+
case 'login': {
|
|
2805
|
+
const asked = str(flags['--scope']);
|
|
2806
|
+
const scopes = asked === undefined ? undefined : asked.split(/[\s,]+/).filter(Boolean);
|
|
2807
|
+
if (scopes !== undefined && scopes.length === 0) {
|
|
2808
|
+
u.error('--scope needs at least one scope', 'funtools login --scope community:read,community:write');
|
|
2809
|
+
return EXIT.error;
|
|
2810
|
+
}
|
|
2811
|
+
if (scopes !== undefined && flags['--key']) {
|
|
2812
|
+
u.error('--scope goes with the browser sign-in', 'a key you paste keeps the scopes it was made with (Settings › Developers)');
|
|
2813
|
+
return EXIT.error;
|
|
2814
|
+
}
|
|
2815
|
+
if (!flags['--key'])
|
|
2816
|
+
return await browserLogin(io, undefined, undefined, undefined, scopes);
|
|
2817
|
+
return await login({ key: str(flags['--key']), api: str(flags['--api']) }, io);
|
|
2818
|
+
}
|
|
2819
|
+
case 'whoami': return await whoami(io);
|
|
2820
|
+
case 'setup': return await (await import("./commands/setup.js")).setup({ host: str(flags['--host']), dryRun: !!flags['--dry-run'], json }, io);
|
|
2821
|
+
case 'mcp': {
|
|
2822
|
+
const { serveStdio } = await import("./mcp/server.js");
|
|
2823
|
+
await serveStdio({ cwd: process.cwd(), bin: [process.execPath, ...process.execArgv, process.argv[1]] });
|
|
2824
|
+
return EXIT.pass;
|
|
2825
|
+
}
|
|
2826
|
+
case 'find': return await (await import("./commands/find.js")).find({ ask: positionals.join(' '), usage: USAGE, names: Object.keys(SPECS), json }, io);
|
|
2827
|
+
case 'open': {
|
|
2828
|
+
const what = positionals[0];
|
|
2829
|
+
if (!what) {
|
|
2830
|
+
u.error('open needs a URL or a file', 'funtools open http://localhost:5317/ | funtools open design/first-look/target-key-art.png');
|
|
2831
|
+
return EXIT.error;
|
|
2832
|
+
}
|
|
2833
|
+
const target = /^[a-z]+:\/\//i.test(what) ? what : (await import('node:path')).resolve(what);
|
|
2834
|
+
if (!/^[a-z]+:\/\//i.test(what) && !(await import('node:fs')).existsSync(target)) {
|
|
2835
|
+
u.error(`no such file: ${what}`, 'give a URL or a path that exists');
|
|
2836
|
+
return EXIT.error;
|
|
2837
|
+
}
|
|
2838
|
+
const { openTarget } = await import("./dev/open.js");
|
|
2839
|
+
if (openTarget(target)) {
|
|
2840
|
+
u.summary('pass', `opened ${what}`);
|
|
2841
|
+
return EXIT.pass;
|
|
2842
|
+
}
|
|
2843
|
+
u.summary('note', `nothing opens here (CI, FUNPIXEL_NO_OPEN, or no display): ${what}`);
|
|
2844
|
+
return EXIT.not_observed;
|
|
2845
|
+
}
|
|
2846
|
+
case 'usage': return await usage({ days: str(flags['--days']), thisKey: !!flags['--this-key'], json }, io);
|
|
2847
|
+
case 'budget': return await budget({ json }, io);
|
|
2848
|
+
case 'logout': return await logout(io);
|
|
2849
|
+
case 'publish': {
|
|
2850
|
+
const to = str(flags['--target']) ?? 'funpixel';
|
|
2851
|
+
if (!TARGETS.includes(to)) {
|
|
2852
|
+
u.error(`unknown target: ${to}`, `use one of ${TARGETS.join(', ')}`);
|
|
2853
|
+
return EXIT.error;
|
|
2854
|
+
}
|
|
2855
|
+
return await publish({
|
|
2856
|
+
target, to: to, yes: !!flags['--yes'], json,
|
|
2857
|
+
name: str(flags['--name']), gameId: str(flags['--game-id']),
|
|
2858
|
+
itch: str(flags['--itch']), project: str(flags['--project']), out: str(flags['--out']),
|
|
2859
|
+
}, io);
|
|
2860
|
+
}
|
|
2861
|
+
case 'models': {
|
|
2862
|
+
const sub = positionals[0];
|
|
2863
|
+
if (!sub || !MODELS_SUBCOMMANDS.includes(sub)) {
|
|
2864
|
+
u.error(sub ? `unknown models step: ${sub}` : 'models needs a step', 'funtools models list | models install <local id>');
|
|
2865
|
+
return EXIT.error;
|
|
2866
|
+
}
|
|
2867
|
+
if (positionals.length > (sub === 'install' ? 2 : 1)) {
|
|
2868
|
+
u.error(`models ${sub} takes ${sub === 'install' ? 'one model id' : 'no argument'}`, 'funtools models list | models install kokoro-82m');
|
|
2869
|
+
return EXIT.error;
|
|
2870
|
+
}
|
|
2871
|
+
return await models({ sub, id: positionals[1], force: !!flags['--force'], json }, io);
|
|
2872
|
+
}
|
|
2873
|
+
case 'store': {
|
|
2874
|
+
const sub = positionals[0];
|
|
2875
|
+
if (!sub || !STORE_SUBCOMMANDS.includes(sub)) {
|
|
2876
|
+
u.error(sub ? `unknown store step: ${sub}` : 'store needs a step', 'funtools store init|doctor|lint|plan [dir] [--store play|ios|steam]');
|
|
2877
|
+
return EXIT.error;
|
|
2878
|
+
}
|
|
2879
|
+
if (positionals.length > 2) {
|
|
2880
|
+
u.error(`store ${sub} takes one folder`, `funtools store ${sub} [dir]`);
|
|
2881
|
+
return EXIT.error;
|
|
2882
|
+
}
|
|
2883
|
+
return await store({ sub, dir: str(flags['--dir']) ?? positionals[1], store: str(flags['--store']), json }, io);
|
|
2884
|
+
}
|
|
2885
|
+
default: {
|
|
2886
|
+
const viewport = str(flags['--viewport']) ?? 'desktop';
|
|
2887
|
+
if (!VIEWPORT_CHOICES.includes(viewport)) {
|
|
2888
|
+
u.error(`unknown viewport: ${viewport}`, '--viewport is desktop, phone or both');
|
|
2889
|
+
return EXIT.error;
|
|
2890
|
+
}
|
|
2891
|
+
const players = str(flags['--players']);
|
|
2892
|
+
if (players !== undefined && players !== '2') {
|
|
2893
|
+
u.error(`--players ${players}`, 'two players is what verify measures: --players 2');
|
|
2894
|
+
return EXIT.error;
|
|
2895
|
+
}
|
|
2896
|
+
return await verify({
|
|
2897
|
+
players: players ? 2 : undefined,
|
|
2898
|
+
target, checks: str(flags['--checks']), manifest: str(flags['--manifest']), sweep: !flags['--no-sweep'], watch: !!flags['--watch'],
|
|
2899
|
+
json, report: !!flags['--report'], viewport: viewport, frontDoor: frontDoorOf(flags),
|
|
2900
|
+
}, io);
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
catch (e) {
|
|
2905
|
+
const m = message(e), dash = m.indexOf(' — ');
|
|
2906
|
+
u.error(dash < 0 ? m : m.slice(0, dash), dash < 0 ? 'run `funtools doctor` to check the setup; if it repeats, report it with the command you ran' : m.slice(dash + 3));
|
|
2907
|
+
return EXIT.error;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
const invokedDirectly = () => { try {
|
|
2911
|
+
return !!process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
2912
|
+
}
|
|
2913
|
+
catch {
|
|
2914
|
+
return false;
|
|
2915
|
+
} };
|
|
2916
|
+
if (invokedDirectly())
|
|
2917
|
+
process.exitCode = await main(process.argv.slice(2));
|