lecodes-sdk 0.20.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/global.d.ts +35 -9
- package/dist/types/animate/tween/Animation.d.ts +69 -0
- package/dist/types/animate/tween/Timeline.d.ts +55 -0
- package/dist/types/animate/tween/animateValue.d.ts +27 -0
- package/dist/types/animate/tween/easing.d.ts +29 -0
- package/dist/types/animate/tween/spec.d.ts +178 -0
- package/dist/types/canvas/Canvas.d.ts +2 -0
- package/dist/types/g2/Node2D.d.ts +16 -0
- package/dist/types/g2/Sprite.d.ts +11 -1
- package/dist/types/gl/Camera.d.ts +15 -1
- package/dist/types/gl/DecalSet.d.ts +48 -3
- package/dist/types/gl/Foliage.d.ts +47 -0
- package/dist/types/gl/Geometry.d.ts +36 -0
- package/dist/types/gl/Light.d.ts +25 -7
- package/dist/types/gl/Lightmap.d.ts +90 -51
- package/dist/types/gl/Material.d.ts +32 -20
- package/dist/types/gl/Mesh.d.ts +7 -1
- package/dist/types/gl/Model.d.ts +41 -5
- package/dist/types/gl/Node.d.ts +18 -0
- package/dist/types/gl/Particles.d.ts +53 -1
- package/dist/types/gl/Scene.d.ts +21 -1
- package/dist/types/gl/animation/AnimationClip.d.ts +19 -0
- package/dist/types/gl/animation/Animator.d.ts +27 -0
- package/dist/types/gl/animation/DynamicBone.d.ts +184 -0
- package/dist/types/gl/animation/IK.d.ts +109 -0
- package/dist/types/gl/{Locomotion.d.ts → animation/Locomotion.d.ts} +6 -6
- package/dist/types/gl/animation/Warp.d.ts +2 -1
- package/dist/types/gl/animation/core.d.ts +35 -4
- package/dist/types/gl/{AudioSource.d.ts → audio/AudioSource.d.ts} +6 -6
- package/dist/types/gl/{AudioZone.d.ts → audio/AudioZone.d.ts} +4 -4
- package/dist/types/gl/{SceneAudio.d.ts → audio/SceneAudio.d.ts} +1 -1
- package/dist/types/gl/{NavAgent.d.ts → nav/NavAgent.d.ts} +4 -4
- package/dist/types/gl/{NavMesh.d.ts → nav/NavMesh.d.ts} +4 -4
- package/dist/types/gl/{CharacterController.d.ts → physics/CharacterController.d.ts} +5 -5
- package/dist/types/gl/{Physics.d.ts → physics/Physics.d.ts} +6 -5
- package/dist/types/gl/physics/Ragdoll.d.ts +161 -0
- package/dist/types/gl/{Shape.d.ts → physics/Shape.d.ts} +3 -3
- package/dist/types/gl/{Trigger.d.ts → physics/Trigger.d.ts} +2 -2
- package/dist/types/gl/{Terrain.d.ts → terrain/Terrain.d.ts} +10 -8
- package/dist/types/gl/{terrainMesh.d.ts → terrain/terrainMesh.d.ts} +1 -1
- package/dist/types/gl/vehicle/Vehicle.d.ts +300 -0
- package/dist/types/gl/vehicle/Wheel.d.ts +147 -0
- package/dist/types/inject.d.ts +35 -27
- package/dist/types/runtime/files.d.ts +24 -1
- package/dist/types/scene/defineScene.d.ts +50 -31
- package/dist/types/ui/UIButton.d.ts +3 -1
- package/dist/types/ui/UIInput.d.ts +5 -1
- package/dist/types/ui/UINode.d.ts +24 -24
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/prompts/README.md +142 -142
- package/prompts/core-design.md +27 -4
- package/prompts/core.md +35 -6
- package/prompts/select.ts +19 -4
- package/src/animate/tween/Animation.ts +378 -0
- package/src/animate/tween/Timeline.ts +175 -0
- package/src/animate/tween/animateValue.ts +100 -0
- package/src/animate/tween/easing.ts +172 -0
- package/src/animate/tween/spec.ts +479 -0
- package/src/bridges.d.ts +1760 -1481
- package/src/canvas/Canvas.ts +21 -0
- package/src/compile/__tests__/assetMacro.test.ts +26 -0
- package/src/compile/__tests__/compile.test.ts +11 -0
- package/src/compile/__tests__/detectEntry.test.ts +19 -0
- package/src/compile/__tests__/serverSplit.test.ts +27 -0
- package/src/compile/bundler.ts +34 -4
- package/src/compile/compileProject.ts +31 -1
- package/src/compile/detectEntry.ts +8 -3
- package/src/compile/header.ts +6 -3
- package/src/compile/index.ts +3 -0
- package/src/compile/sceneEditor.ts +42 -1
- package/src/compile/serverSplit.ts +9 -3
- package/src/g2/Node2D.ts +38 -0
- package/src/g2/Sprite.ts +20 -1
- package/src/gl/Camera.ts +34 -1
- package/src/gl/DecalSet.ts +132 -5
- package/src/gl/Foliage.ts +102 -0
- package/src/gl/Geometry.ts +109 -0
- package/src/gl/Light.ts +46 -16
- package/src/gl/Lightmap.ts +440 -249
- package/src/gl/Material.ts +69 -36
- package/src/gl/Mesh.ts +120 -102
- package/src/gl/Model.ts +167 -124
- package/src/gl/Node.ts +40 -1
- package/src/gl/Particles.ts +82 -5
- package/src/gl/Scene.ts +35 -2
- package/src/gl/animation/AnimationClip.ts +52 -0
- package/src/gl/animation/Animator.ts +42 -2
- package/src/gl/animation/DynamicBone.ts +482 -0
- package/src/gl/animation/IK.ts +214 -0
- package/src/gl/{Locomotion.ts → animation/Locomotion.ts} +7 -7
- package/src/gl/animation/Playback.ts +5 -4
- package/src/gl/animation/Warp.ts +5 -2
- package/src/gl/animation/core.ts +65 -4
- package/src/gl/{AudioSource.ts → audio/AudioSource.ts} +7 -7
- package/src/gl/{AudioZone.ts → audio/AudioZone.ts} +75 -75
- package/src/gl/{SceneAudio.ts → audio/SceneAudio.ts} +2 -2
- package/src/gl/{NavAgent.ts → nav/NavAgent.ts} +5 -5
- package/src/gl/{NavMesh.ts → nav/NavMesh.ts} +8 -8
- package/src/gl/{CharacterController.ts → physics/CharacterController.ts} +5 -5
- package/src/gl/{Physics.ts → physics/Physics.ts} +12 -5
- package/src/gl/physics/Ragdoll.ts +451 -0
- package/src/gl/{Shape.ts → physics/Shape.ts} +3 -3
- package/src/gl/{Trigger.ts → physics/Trigger.ts} +45 -45
- package/src/gl/{physicsEvents.ts → physics/physicsEvents.ts} +1 -1
- package/src/gl/{Terrain.ts → terrain/Terrain.ts} +14 -12
- package/src/gl/{terrainMesh.ts → terrain/terrainMesh.ts} +1 -1
- package/src/gl/vehicle/Vehicle.ts +666 -0
- package/src/gl/vehicle/Wheel.ts +290 -0
- package/src/inject.ts +236 -224
- package/src/runtime/files.ts +32 -2
- package/src/scene/defineScene.ts +92 -66
- package/src/scene/level.ts +2 -2
- package/src/ui/UIButton.ts +2 -2
- package/src/ui/UIInput.ts +3 -3
- package/src/ui/UINode.ts +61 -36
- package/dist/types/animate/animate.d.ts +0 -20
- package/dist/types/gl/Gearbox.d.ts +0 -86
- package/dist/types/gl/IK.d.ts +0 -53
- package/dist/types/gl/Ragdoll.d.ts +0 -86
- package/dist/types/gl/Vehicle.d.ts +0 -191
- package/dist/types/gl/Wheel.d.ts +0 -95
- package/src/animate/animate.ts +0 -238
- package/src/gl/Gearbox.ts +0 -212
- package/src/gl/IK.ts +0 -193
- package/src/gl/Ragdoll.ts +0 -270
- package/src/gl/Vehicle.ts +0 -473
- package/src/gl/Wheel.ts +0 -240
- /package/dist/types/gl/{physicsEvents.d.ts → physics/physicsEvents.d.ts} +0 -0
package/package.json
CHANGED
package/prompts/README.md
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
# Codegen prompts
|
|
2
|
-
|
|
3
|
-
> **Scope: the le.codes PLATFORM assistant only.** These prompts are bundled into `dist/` and served
|
|
4
|
-
> by claude-proxy to the in-editor chat. They are NOT used by local development — Claude Code and
|
|
5
|
-
> `lecodes-cli` never read them; those get the SDK surface from `.lecodes/types` (the d.ts bundle),
|
|
6
|
-
> the generated project `CLAUDE.md` (`lecodes-cli/src/commands/
|
|
7
|
-
> A change meant for local development belongs there, not here.
|
|
8
|
-
|
|
9
|
-
The system prompt(s) sent to Claude for LeCodes code generation, split into composable modules so
|
|
10
|
-
a request only pays for the API areas it can actually use. Facts come from `../docs/` (the source
|
|
11
|
-
of truth) — when the SDK changes, update docs first, then mirror the change here.
|
|
12
|
-
|
|
13
|
-
**Consumers:**
|
|
14
|
-
- `claude-proxy/prompts/*.md` — deployed copies of `dist/` + `router.md` (see its README); the
|
|
15
|
-
proxy injects them per the `x-lecodes-bundle` header (`claude-proxy/src/proxy/bundles.ts`). An
|
|
16
|
-
unknown/missing header is a **400** (no silent fallback), and the proxy's `/health` exposes a
|
|
17
|
-
sha256-12 per bundle — `bun run check:prompts` (repo root) compares them against `dist/` to
|
|
18
|
-
catch deploy drift (`--dir <proxy>/prompts` checks a local checkout instead).
|
|
19
|
-
- `packages/frontend/.../chat/engine/bundleSelect.ts` — a thin adapter over `select.ts` (imported
|
|
20
|
-
as `sdk/prompts/select`); the selection logic itself is not duplicated anywhere.
|
|
21
|
-
|
|
22
|
-
## Layout
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
core.md shared foundation: response format, entry-point rule, runtime, math, tweens
|
|
26
|
-
index.md one-line map of EVERY global — always included (anti-hallucination anchor)
|
|
27
|
-
canvas.md Canvas drawing surface — engine-independent, in every bundle (each engine
|
|
28
|
-
module documents its own hookup: Sprite texture / UIImage src / Texture)
|
|
29
|
-
ui.md UI module (screens, router, styling, widgets, lists)
|
|
30
|
-
2d.md 2D engine module (sprites, tilemaps, Box2D physics, aspects, Canvas)
|
|
31
|
-
3d.md SHARED 3D content (nodes, meshes, models, materials, physics, particles)
|
|
32
|
-
3d-scene.md non-AR scenes: Scene construction, camera control, CharacterController, example
|
|
33
|
-
3d-scene-files.md declarative .scene.ts files (defineScene/use/ref/make, SceneHandle, HUD-over-scene)
|
|
34
|
-
— the visual scene editor's format; non-AR only (camera nodes, skybox env)
|
|
35
|
-
ar.md AR scenes: ARScene, anchors, placement controls, example — never with 3d-scene.md
|
|
36
|
-
core-design.md design mode's core: directives/context/reports, no runtime APIs (no timers/network)
|
|
37
|
-
ui-design.md design mode's UI: curated static-mockup subset of ui.md (KEEP IN SYNC with it)
|
|
38
|
-
design.md design mode: canvas conventions — kit-first, states, design_map tool, tab bar
|
|
39
|
-
concept.md concept mode: product-shaping conversation, writes ONLY design/spec.md
|
|
40
|
-
compose.ts builds dist/<bundle>.md from the modules
|
|
41
|
-
select.ts deterministic bundle detection from project code
|
|
42
|
-
router-prompt.md fallback classifier prompt (empty projects only)
|
|
43
|
-
namer-prompt.md project name + icon-emoji generator (first request of a new project)
|
|
44
|
-
intake-prompt.md new-project intake: clarifying questions + confirmable brief (two Haiku turns)
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Bundles (`bun run prompts/compose.ts` → `dist/`)
|
|
48
|
-
|
|
49
|
-
| Bundle | Modules | For |
|
|
50
|
-
|---|---|---|
|
|
51
|
-
| `ui-app` | core + index + canvas + ui | apps: screens, forms, lists, chat, commerce |
|
|
52
|
-
| `2d-game` | core + index + canvas + 2d + ui | flat games, drawing toys |
|
|
53
|
-
| `3d-app` | core + index + canvas + 3d + 3d-scene + 3d-scene-files + ui | 3D games/viewers, scene-editor projects |
|
|
54
|
-
| `ar-app` | core + index + canvas + 3d + ar + ui | AR experiences |
|
|
55
|
-
| `design` | core-design + ui-design + design | design mode: the design/ prototyping canvas |
|
|
56
|
-
| `concept` | concept | concept mode: shape the idea, maintain design/spec.md (~0.8k tokens) |
|
|
57
|
-
|
|
58
|
-
Fixed bundles (not per-request assembly) are deliberate: **each bundle is a stable prompt prefix**,
|
|
59
|
-
so Anthropic prompt caching hits on every generation after the first (~10× cheaper input, lower
|
|
60
|
-
latency). Per-request cherry-picking would produce near-unique prefixes and always pay full price.
|
|
61
|
-
Game bundles include the full UI module because games need HUDs/menus; the ui-app bundle omits the
|
|
62
|
-
engines entirely.
|
|
63
|
-
|
|
64
|
-
## Selecting the bundle
|
|
65
|
-
|
|
66
|
-
`design` and `concept` are different from the engine bundles: they are selected EXPLICITLY by the
|
|
67
|
-
client from the editor's mode switcher (Concept / Design / Build — `x-lecodes-bundle: design` or
|
|
68
|
-
`concept`) — never routed, detected from code, or reachable via the `<bundle>` escalation. The
|
|
69
|
-
engine ladder below applies to Build mode only. Concept turns also carry no code context (design
|
|
70
|
-
excerpts + file names only) and the client enforces each mode's write boundary (concept:
|
|
71
|
-
design/spec.md only; design: design/ only). Any bundle may end a reply with `<mode>x</mode>` — the
|
|
72
|
-
client strips it and renders a "Switch to …" button; the mode never switches itself.
|
|
73
|
-
|
|
74
|
-
For the engine bundles — three signals, in this order:
|
|
75
|
-
|
|
76
|
-
**1. Project code — `select.ts` (primary, deterministic).**
|
|
77
|
-
`detectBundle(files)` scans the project's `.ts` sources for engine globals (comments and string
|
|
78
|
-
literals stripped) and returns the bundle. This is language-independent — it doesn't matter what
|
|
79
|
-
language the user writes in, because it never reads the user's words at all. It covers every
|
|
80
|
-
request against an existing project, i.e. the vast majority of traffic. Priority: AR > 3D > 2D;
|
|
81
|
-
sources with no engine references → `ui-app`. This mirrors the engine-kind detection the compiler
|
|
82
|
-
already does for project icons — if that detection is exposed server-side, reuse it instead.
|
|
83
|
-
|
|
84
|
-
**0. Intake — `intake-prompt.md` (new projects, preferred over the router).**
|
|
85
|
-
The session's first request against a project with no app code runs the intake instead of building:
|
|
86
|
-
one Haiku call returns 2–4 clarifying questions with tappable options (including the app kind when
|
|
87
|
-
the request is ambiguous between kinds), a second call turns the answers into a short brief the
|
|
88
|
-
user confirms. The confirmed kind sets the first build turn's bundle explicitly (`x-lecodes-bundle`
|
|
89
|
-
sent by the client — the router below never runs), and the brief seeds `design/spec.md` as the
|
|
90
|
-
project concept. Intake failure or the user's Skip degrades to the flow below — it never blocks.
|
|
91
|
-
Client side: `frontend/.../chat/engine/intake.ts` + the intake card in `chatController.ts`.
|
|
92
|
-
|
|
93
|
-
**2. Router model — `router-prompt.md` (fallback: intake skipped/failed on empty projects).**
|
|
94
|
-
When `detectBundle` returns `null` (no code yet), classify the user's first message with one cheap
|
|
95
|
-
call: `claude-haiku-4-5`, temperature 0, `max_tokens: 8`, reply is a single word `ui|2d|3d|ar`.
|
|
96
|
-
The prompt is meaning-based and multilingual (EN/RU examples included). Parse defensively; any
|
|
97
|
-
garbage → `ui`. Cost/latency: ~1k input tokens, a few output tokens, ~200–400 ms — negligible, and
|
|
98
|
-
it runs at most once per project's life.
|
|
99
|
-
|
|
100
|
-
**Stickiness.** Cache the chosen bundle per conversation and only move UP
|
|
101
|
-
(`upgradeBundle(current, detected)`: ui → 2d/3d → ar), re-running `detectBundle` over files the
|
|
102
|
-
assistant itself just generated. Never downgrade mid-conversation — it would invalidate the prompt
|
|
103
|
-
cache and confuse in-flight context. Wrong-but-bigger is harmless; wrong-but-smaller causes
|
|
104
|
-
hallucinated APIs.
|
|
105
|
-
|
|
106
|
-
**3. Escalation — the `<bundle>` directive (recovery from a misroute).**
|
|
107
|
-
Code-based detection can never recover from a wrong-but-smaller bundle on its own: a model that
|
|
108
|
-
wasn't shown an engine never emits its globals, so the upgrade never triggers. core.md ("Missing
|
|
109
|
-
engine") therefore instructs the generator to reply with ONLY `<bundle>ar</bundle>` (or `3d`/`2d`)
|
|
110
|
-
when the request needs engine APIs its prompt doesn't document. The client (`parseBundleDirective`
|
|
111
|
-
in `select.ts`) discards that reply, upgrades the pinned bundle, and re-sends the same request
|
|
112
|
-
once — upgrade-only here too; a non-upgradable directive (e.g. a 3D project asking for the 2D
|
|
113
|
-
engine) surfaces as a plain explanation instead. Relatedly, the router's classifier input must be
|
|
114
|
-
ONLY the user's words: the proxy cuts everything before the frontend's `[Request]` marker, so e.g.
|
|
115
|
-
a `.glb` in the project snapshot can't drag an explicit AR request toward `3d`.
|
|
116
|
-
|
|
117
|
-
## Naming new projects
|
|
118
|
-
|
|
119
|
-
`namer-prompt.md` is unrelated to bundle selection but lives here with the other model prompts:
|
|
120
|
-
an assistant request made while the project has NO CODE yet (no non-empty `.ts` file — uploaded
|
|
121
|
-
assets like a `.glb` model don't count as "started") also fires one extra Haiku call in parallel
|
|
122
|
-
(`x-lecodes-bundle: namer`, `chat/engine/projectNamer.ts`) that returns bare JSON `{name, emoji}`;
|
|
123
|
-
the result is saved via the normal project-update API (name + `iconEmoji`, drawn by
|
|
124
|
-
`VProjectIcon`). Uncached — the prompt is far below Haiku's minimum cacheable prefix. Parsing is
|
|
125
|
-
defensive: a garbled reply must never rename the project, so it's rejected rather than truncated
|
|
126
|
-
into place; a rename while the call is in flight also wins over the generated name.
|
|
127
|
-
|
|
128
|
-
**Why not route every request with a model?** It adds a failure mode (misroute → the generator
|
|
129
|
-
confidently invents APIs it wasn't shown), latency, and cost on 100% of traffic to solve a problem
|
|
130
|
-
that exists only for the ~one request per project where no code exists. Residual misroutes are
|
|
131
|
-
recoverable: the index.md map means the generator always knows what exists, and the `<bundle>`
|
|
132
|
-
escalation directive (above) lets it swap in the right engine module instead of guessing.
|
|
133
|
-
|
|
134
|
-
## Keeping prompts honest
|
|
135
|
-
|
|
136
|
-
- `index.md` must list every global exported from `sdk/src/inject.ts` — same contract as
|
|
137
|
-
`docs/check.mjs`. When a global is added, update docs, then the module, then index.md.
|
|
138
|
-
- Known corrections baked into these prompts (do not regress; see `docs/AUTHORING.md`):
|
|
139
|
-
`onEndReached(thresholdPx, cb)` (threshold first), `bgSize: cover|contain|tile` (no `fill`),
|
|
140
|
-
track `deltaX/deltaY` are per-move, engine colors are hex/packed-int only, `animate` can't
|
|
141
|
-
tween strings/colors, `node.physics` (not `.body`), no `Camera2D.follow`, no
|
|
142
|
-
`UIScrollable.scrollTo`, models can't be cloned.
|
|
1
|
+
# Codegen prompts
|
|
2
|
+
|
|
3
|
+
> **Scope: the le.codes PLATFORM assistant only.** These prompts are bundled into `dist/` and served
|
|
4
|
+
> by claude-proxy to the in-editor chat. They are NOT used by local development — Claude Code and
|
|
5
|
+
> `lecodes-cli` never read them; those get the SDK surface from `.lecodes/types` (the d.ts bundle),
|
|
6
|
+
> the generated project `CLAUDE.md` (`lecodes-cli/src/commands/init/templates.ts`) and `sdk/docs`.
|
|
7
|
+
> A change meant for local development belongs there, not here.
|
|
8
|
+
|
|
9
|
+
The system prompt(s) sent to Claude for LeCodes code generation, split into composable modules so
|
|
10
|
+
a request only pays for the API areas it can actually use. Facts come from `../docs/` (the source
|
|
11
|
+
of truth) — when the SDK changes, update docs first, then mirror the change here.
|
|
12
|
+
|
|
13
|
+
**Consumers:**
|
|
14
|
+
- `claude-proxy/prompts/*.md` — deployed copies of `dist/` + `router.md` (see its README); the
|
|
15
|
+
proxy injects them per the `x-lecodes-bundle` header (`claude-proxy/src/proxy/bundles.ts`). An
|
|
16
|
+
unknown/missing header is a **400** (no silent fallback), and the proxy's `/health` exposes a
|
|
17
|
+
sha256-12 per bundle — `bun run check:prompts` (repo root) compares them against `dist/` to
|
|
18
|
+
catch deploy drift (`--dir <proxy>/prompts` checks a local checkout instead).
|
|
19
|
+
- `packages/frontend/.../chat/engine/bundleSelect.ts` — a thin adapter over `select.ts` (imported
|
|
20
|
+
as `sdk/prompts/select`); the selection logic itself is not duplicated anywhere.
|
|
21
|
+
|
|
22
|
+
## Layout
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
core.md shared foundation: response format, entry-point rule, runtime, math, tweens
|
|
26
|
+
index.md one-line map of EVERY global — always included (anti-hallucination anchor)
|
|
27
|
+
canvas.md Canvas drawing surface — engine-independent, in every bundle (each engine
|
|
28
|
+
module documents its own hookup: Sprite texture / UIImage src / Texture)
|
|
29
|
+
ui.md UI module (screens, router, styling, widgets, lists)
|
|
30
|
+
2d.md 2D engine module (sprites, tilemaps, Box2D physics, aspects, Canvas)
|
|
31
|
+
3d.md SHARED 3D content (nodes, meshes, models, materials, physics, particles)
|
|
32
|
+
3d-scene.md non-AR scenes: Scene construction, camera control, CharacterController, example
|
|
33
|
+
3d-scene-files.md declarative .scene.ts files (defineScene/use/ref/make, SceneHandle, HUD-over-scene)
|
|
34
|
+
— the visual scene editor's format; non-AR only (camera nodes, skybox env)
|
|
35
|
+
ar.md AR scenes: ARScene, anchors, placement controls, example — never with 3d-scene.md
|
|
36
|
+
core-design.md design mode's core: directives/context/reports, no runtime APIs (no timers/network)
|
|
37
|
+
ui-design.md design mode's UI: curated static-mockup subset of ui.md (KEEP IN SYNC with it)
|
|
38
|
+
design.md design mode: canvas conventions — kit-first, states, design_map tool, tab bar
|
|
39
|
+
concept.md concept mode: product-shaping conversation, writes ONLY design/spec.md
|
|
40
|
+
compose.ts builds dist/<bundle>.md from the modules
|
|
41
|
+
select.ts deterministic bundle detection from project code
|
|
42
|
+
router-prompt.md fallback classifier prompt (empty projects only)
|
|
43
|
+
namer-prompt.md project name + icon-emoji generator (first request of a new project)
|
|
44
|
+
intake-prompt.md new-project intake: clarifying questions + confirmable brief (two Haiku turns)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Bundles (`bun run prompts/compose.ts` → `dist/`)
|
|
48
|
+
|
|
49
|
+
| Bundle | Modules | For |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `ui-app` | core + index + canvas + ui | apps: screens, forms, lists, chat, commerce |
|
|
52
|
+
| `2d-game` | core + index + canvas + 2d + ui | flat games, drawing toys |
|
|
53
|
+
| `3d-app` | core + index + canvas + 3d + 3d-scene + 3d-scene-files + ui | 3D games/viewers, scene-editor projects |
|
|
54
|
+
| `ar-app` | core + index + canvas + 3d + ar + ui | AR experiences |
|
|
55
|
+
| `design` | core-design + ui-design + design | design mode: the design/ prototyping canvas |
|
|
56
|
+
| `concept` | concept | concept mode: shape the idea, maintain design/spec.md (~0.8k tokens) |
|
|
57
|
+
|
|
58
|
+
Fixed bundles (not per-request assembly) are deliberate: **each bundle is a stable prompt prefix**,
|
|
59
|
+
so Anthropic prompt caching hits on every generation after the first (~10× cheaper input, lower
|
|
60
|
+
latency). Per-request cherry-picking would produce near-unique prefixes and always pay full price.
|
|
61
|
+
Game bundles include the full UI module because games need HUDs/menus; the ui-app bundle omits the
|
|
62
|
+
engines entirely.
|
|
63
|
+
|
|
64
|
+
## Selecting the bundle
|
|
65
|
+
|
|
66
|
+
`design` and `concept` are different from the engine bundles: they are selected EXPLICITLY by the
|
|
67
|
+
client from the editor's mode switcher (Concept / Design / Build — `x-lecodes-bundle: design` or
|
|
68
|
+
`concept`) — never routed, detected from code, or reachable via the `<bundle>` escalation. The
|
|
69
|
+
engine ladder below applies to Build mode only. Concept turns also carry no code context (design
|
|
70
|
+
excerpts + file names only) and the client enforces each mode's write boundary (concept:
|
|
71
|
+
design/spec.md only; design: design/ only). Any bundle may end a reply with `<mode>x</mode>` — the
|
|
72
|
+
client strips it and renders a "Switch to …" button; the mode never switches itself.
|
|
73
|
+
|
|
74
|
+
For the engine bundles — three signals, in this order:
|
|
75
|
+
|
|
76
|
+
**1. Project code — `select.ts` (primary, deterministic).**
|
|
77
|
+
`detectBundle(files)` scans the project's `.ts` sources for engine globals (comments and string
|
|
78
|
+
literals stripped) and returns the bundle. This is language-independent — it doesn't matter what
|
|
79
|
+
language the user writes in, because it never reads the user's words at all. It covers every
|
|
80
|
+
request against an existing project, i.e. the vast majority of traffic. Priority: AR > 3D > 2D;
|
|
81
|
+
sources with no engine references → `ui-app`. This mirrors the engine-kind detection the compiler
|
|
82
|
+
already does for project icons — if that detection is exposed server-side, reuse it instead.
|
|
83
|
+
|
|
84
|
+
**0. Intake — `intake-prompt.md` (new projects, preferred over the router).**
|
|
85
|
+
The session's first request against a project with no app code runs the intake instead of building:
|
|
86
|
+
one Haiku call returns 2–4 clarifying questions with tappable options (including the app kind when
|
|
87
|
+
the request is ambiguous between kinds), a second call turns the answers into a short brief the
|
|
88
|
+
user confirms. The confirmed kind sets the first build turn's bundle explicitly (`x-lecodes-bundle`
|
|
89
|
+
sent by the client — the router below never runs), and the brief seeds `design/spec.md` as the
|
|
90
|
+
project concept. Intake failure or the user's Skip degrades to the flow below — it never blocks.
|
|
91
|
+
Client side: `frontend/.../chat/engine/intake.ts` + the intake card in `chatController.ts`.
|
|
92
|
+
|
|
93
|
+
**2. Router model — `router-prompt.md` (fallback: intake skipped/failed on empty projects).**
|
|
94
|
+
When `detectBundle` returns `null` (no code yet), classify the user's first message with one cheap
|
|
95
|
+
call: `claude-haiku-4-5`, temperature 0, `max_tokens: 8`, reply is a single word `ui|2d|3d|ar`.
|
|
96
|
+
The prompt is meaning-based and multilingual (EN/RU examples included). Parse defensively; any
|
|
97
|
+
garbage → `ui`. Cost/latency: ~1k input tokens, a few output tokens, ~200–400 ms — negligible, and
|
|
98
|
+
it runs at most once per project's life.
|
|
99
|
+
|
|
100
|
+
**Stickiness.** Cache the chosen bundle per conversation and only move UP
|
|
101
|
+
(`upgradeBundle(current, detected)`: ui → 2d/3d → ar), re-running `detectBundle` over files the
|
|
102
|
+
assistant itself just generated. Never downgrade mid-conversation — it would invalidate the prompt
|
|
103
|
+
cache and confuse in-flight context. Wrong-but-bigger is harmless; wrong-but-smaller causes
|
|
104
|
+
hallucinated APIs.
|
|
105
|
+
|
|
106
|
+
**3. Escalation — the `<bundle>` directive (recovery from a misroute).**
|
|
107
|
+
Code-based detection can never recover from a wrong-but-smaller bundle on its own: a model that
|
|
108
|
+
wasn't shown an engine never emits its globals, so the upgrade never triggers. core.md ("Missing
|
|
109
|
+
engine") therefore instructs the generator to reply with ONLY `<bundle>ar</bundle>` (or `3d`/`2d`)
|
|
110
|
+
when the request needs engine APIs its prompt doesn't document. The client (`parseBundleDirective`
|
|
111
|
+
in `select.ts`) discards that reply, upgrades the pinned bundle, and re-sends the same request
|
|
112
|
+
once — upgrade-only here too; a non-upgradable directive (e.g. a 3D project asking for the 2D
|
|
113
|
+
engine) surfaces as a plain explanation instead. Relatedly, the router's classifier input must be
|
|
114
|
+
ONLY the user's words: the proxy cuts everything before the frontend's `[Request]` marker, so e.g.
|
|
115
|
+
a `.glb` in the project snapshot can't drag an explicit AR request toward `3d`.
|
|
116
|
+
|
|
117
|
+
## Naming new projects
|
|
118
|
+
|
|
119
|
+
`namer-prompt.md` is unrelated to bundle selection but lives here with the other model prompts:
|
|
120
|
+
an assistant request made while the project has NO CODE yet (no non-empty `.ts` file — uploaded
|
|
121
|
+
assets like a `.glb` model don't count as "started") also fires one extra Haiku call in parallel
|
|
122
|
+
(`x-lecodes-bundle: namer`, `chat/engine/projectNamer.ts`) that returns bare JSON `{name, emoji}`;
|
|
123
|
+
the result is saved via the normal project-update API (name + `iconEmoji`, drawn by
|
|
124
|
+
`VProjectIcon`). Uncached — the prompt is far below Haiku's minimum cacheable prefix. Parsing is
|
|
125
|
+
defensive: a garbled reply must never rename the project, so it's rejected rather than truncated
|
|
126
|
+
into place; a rename while the call is in flight also wins over the generated name.
|
|
127
|
+
|
|
128
|
+
**Why not route every request with a model?** It adds a failure mode (misroute → the generator
|
|
129
|
+
confidently invents APIs it wasn't shown), latency, and cost on 100% of traffic to solve a problem
|
|
130
|
+
that exists only for the ~one request per project where no code exists. Residual misroutes are
|
|
131
|
+
recoverable: the index.md map means the generator always knows what exists, and the `<bundle>`
|
|
132
|
+
escalation directive (above) lets it swap in the right engine module instead of guessing.
|
|
133
|
+
|
|
134
|
+
## Keeping prompts honest
|
|
135
|
+
|
|
136
|
+
- `index.md` must list every global exported from `sdk/src/inject.ts` — same contract as
|
|
137
|
+
`docs/check.mjs`. When a global is added, update docs, then the module, then index.md.
|
|
138
|
+
- Known corrections baked into these prompts (do not regress; see `docs/AUTHORING.md`):
|
|
139
|
+
`onEndReached(thresholdPx, cb)` (threshold first), `bgSize: cover|contain|tile` (no `fill`),
|
|
140
|
+
track `deltaX/deltaY` are per-move, engine colors are hex/packed-int only, `animate` can't
|
|
141
|
+
tween strings/colors, `node.physics` (not `.body`), no `Camera2D.follow`, no
|
|
142
|
+
`UIScrollable.scrollTo`, models can't be cloned.
|
package/prompts/core-design.md
CHANGED
|
@@ -24,20 +24,33 @@ Remove a single top-level declaration:
|
|
|
24
24
|
|
|
25
25
|
<remove file="design/shared/ui.ts" signature="const unusedCard" />
|
|
26
26
|
|
|
27
|
+
Replace an exact snippet INSIDE a file — the operation for a small change to a screen (a label, a color, one element), where re-emitting the screen would repeat everything else:
|
|
28
|
+
|
|
29
|
+
<replace file="design/screens/home.ts">
|
|
30
|
+
<old>
|
|
31
|
+
UIText("Balance", { size: 16 }),
|
|
32
|
+
</old>
|
|
33
|
+
<new>
|
|
34
|
+
UIText("Group balance", { size: 18, weight: "bold" }),
|
|
35
|
+
</new>
|
|
36
|
+
</replace>
|
|
37
|
+
|
|
27
38
|
Rules:
|
|
28
39
|
- <file> replaces the whole file — write it out in full, never elide with "// ... rest unchanged"
|
|
40
|
+
- <replace>: <old> is copied VERBATIM from the file as it is now (same characters, same lines — indentation is forgiven, nothing else is) and must occur exactly once — quote enough surrounding lines to make it unique; <new> is what takes its place and is never empty (to delete lines, quote the surrounding lines in <old> and repeat them without the deleted ones in <new>). One <replace> per spot; several spots in one file = several <replace> tags, each quoting the file as it was before your reply (they apply in order, so never let one <old> depend on an earlier <new>)
|
|
41
|
+
- A <replace> ALWAYS holds both parts INSIDE the same tag, in this order: <old>…</old> then <new>…</new>. A <replace> without those inner tags is invalid and applies nothing — never emit the old text in one <replace> and the new text in a second one
|
|
29
42
|
- <edit> and <remove> target exactly ONE top-level declaration (function / const / let in global scope). The signature is matched against the start of the existing declaration; the operation then covers that whole declaration — from its first line to its end (closing brace for functions/objects) — and nothing else: never neighboring declarations or surrounding comments
|
|
30
43
|
- The signature only needs the declaration's keyword and name (`const PrimaryButton`) — anything after the name is ignored. It must name a declaration that exists in the file right now
|
|
31
44
|
- <edit> must contain the complete new declaration, not a fragment. After a rename or argument change, update every call site, each via its own <edit>
|
|
32
45
|
- One tag per declaration. To change or remove several declarations, emit several tags
|
|
33
|
-
-
|
|
34
|
-
-
|
|
46
|
+
- Choosing the operation, cheapest first: <replace> for a change of a few lines anywhere — this is how a screen is tweaked (its default export is NOT targetable by <edit>); <edit>/<remove> when a whole small declaration in `shared/` changes shape; <file> for new screens and real restructuring. Never bend <edit> to cover multiple declarations; if nothing else fits, fall back to <file>
|
|
47
|
+
- What you write is the expensive part of a turn: never re-emit a file the request didn't change, and never rewrite a whole screen to touch a few lines
|
|
35
48
|
- Only raw file content inside tags, no markdown fences (```). Backticks for template literals in the code are fine, and non-TS assets (e.g. raw SVG XML in a .svg file) are allowed
|
|
36
49
|
- `design/meta.json` is the one file you NEVER write with these tags — the map is edited only through the `design_map` tool (its positions belong to the developer, and the board may have changed it while you were replying)
|
|
37
50
|
|
|
38
51
|
## Entry point & imports
|
|
39
52
|
|
|
40
|
-
There is NO entry file in a design: every screen file default-exports its screen and nothing calls `.open()` — the board runs them. The UI API (`UIScreen`, `UIText`, …) is global — no imports needed. A screen's only imports are relative paths into `../shared/`. Imports of the design's own files are managed automatically: if your <edit> makes code reference another file's export, the import is added for you.
|
|
53
|
+
There is NO entry file in a design: every screen file default-exports its screen and nothing calls `.open()` — the board runs them. The UI API (`UIScreen`, `UIText`, …) is global — no imports needed. A screen's only imports are relative paths into `../shared/`. Imports of the design's own files are managed automatically: if your <edit> or <replace> makes code reference another file's export, the import is added for you.
|
|
41
54
|
|
|
42
55
|
Reference a design asset (image, .svg) by importing it or with the inline `asset('./path')` macro — a compile-time equivalent of the import (string LITERAL only, never a variable). External `https://…` URLs are used directly as strings.
|
|
43
56
|
|
|
@@ -47,7 +60,7 @@ Each request carries the project state: `[Assets]` lists binary files by path; `
|
|
|
47
60
|
|
|
48
61
|
## Automatic reports
|
|
49
62
|
|
|
50
|
-
A user message starting with `[Automatic report]` is machine-generated feedback from the platform, not the user. After every reply, the platform compiles each changed screen and sends any failure back as such a report. Fix the problem directly with <
|
|
63
|
+
A user message starting with `[Automatic report]` is machine-generated feedback from the platform, not the user. After every reply, the platform compiles each changed screen and sends any failure back as such a report. Fix the problem directly with <replace>/<edit>/<file> operations; at most one short sentence of explanation, never apologize or ask for confirmation. A report that a <replace> or <edit> did NOT apply means the file is unchanged there: re-quote the <old> text exactly from the current file, or name an existing declaration — don't repeat the same tag.
|
|
51
64
|
- A report is a checker result, not a person. Fix exactly what it lists.
|
|
52
65
|
- If a report repeats an error you already tried to fix, take a DIFFERENT approach — prefer rewriting the whole file with <file>.
|
|
53
66
|
- If a report says your reply was cut off and asks you to continue, re-emit the interrupted <file> block from its very beginning (a re-opened <file> replaces the whole file — never continue a file mid-line).
|
|
@@ -56,6 +69,16 @@ A user message starting with `[Automatic report]` is machine-generated feedback
|
|
|
56
69
|
|
|
57
70
|
You may be given tools (they appear in the API request, each with its own description). `design_map` is part of the normal design workflow — use it in the same reply that creates or rewires screens. Any other tool is only worth reaching for when you genuinely can't proceed without it. Act on a tool's result and carry on; don't thank or apologise to it.
|
|
58
71
|
|
|
72
|
+
## What the platform builds
|
|
73
|
+
|
|
74
|
+
LeCodes builds mobile apps AND games: screen-based apps, 2D games, real-time 3D scenes with physics
|
|
75
|
+
and animation, and AR — Build mode has a full engine for each. The board you work on holds SCREENS
|
|
76
|
+
only: menus, HUD, inventory, profile, shop, settings. A 3D world, a game level or an AR scene is not
|
|
77
|
+
a screen; it is built in Build mode, from this design. So for a game or a 3D request, design its
|
|
78
|
+
screens here, describe the scene and its rules in spec.md, and say plainly that the scene itself
|
|
79
|
+
comes in Build mode. Never tell the user the platform cannot do 3D or games, and never pass off a
|
|
80
|
+
static illustration as the scene they asked for.
|
|
81
|
+
|
|
59
82
|
## Modes
|
|
60
83
|
|
|
61
84
|
The platform runs the conversation in one of three modes the user switches between: Concept (shaping what the product is), Design — this prompt, and Build (the working app). When a request is really another mode's job — they want working logic, real data, or to "make the app actually do X" (a design is static mockups; never fake behavior), or they want to rethink what the product is before sketching more — do the part that belongs in the design (or answer briefly) and append the directive as the LAST line of your reply:
|
package/prompts/core.md
CHANGED
|
@@ -24,18 +24,43 @@ Remove a single top-level declaration:
|
|
|
24
24
|
|
|
25
25
|
<remove file="home.ts" signature="let lastTime" />
|
|
26
26
|
|
|
27
|
+
Replace an exact snippet INSIDE a file — the operation for a small change deep in a large declaration (a screen function, a scene setup), where <edit> would re-emit hundreds of untouched lines:
|
|
28
|
+
|
|
29
|
+
<replace file="screens/group.ts">
|
|
30
|
+
<old>
|
|
31
|
+
UIText("Balance", { size: 16 }),
|
|
32
|
+
</old>
|
|
33
|
+
<new>
|
|
34
|
+
UIText("Group balance", { size: 18, weight: "bold" }),
|
|
35
|
+
</new>
|
|
36
|
+
</replace>
|
|
37
|
+
|
|
27
38
|
Rules:
|
|
28
39
|
- <file> replaces the whole file — write it out in full, never elide with "// ... rest unchanged"
|
|
40
|
+
- <replace>: <old> is copied VERBATIM from the file as it is now (same characters, same lines — indentation is forgiven, nothing else is) and must occur exactly once — quote enough surrounding lines to make it unique; <new> is what takes its place and is never empty (to delete lines, quote the surrounding lines in <old> and repeat them without the deleted ones in <new>). One <replace> per spot; several spots in one file = several <replace> tags, each quoting the file as it was before your reply (they apply in order, so never let one <old> depend on an earlier <new>)
|
|
41
|
+
- A <replace> ALWAYS holds both parts INSIDE the same tag, in this order: <old>…</old> then <new>…</new>. A <replace> without those inner tags is invalid and applies nothing — never emit the old text in one <replace> and the new text in a second one
|
|
29
42
|
- <edit> and <remove> target exactly ONE top-level declaration (function / const / let in global scope). The signature is matched against the start of the existing declaration; the operation then covers that whole declaration — from its first line to its end (closing brace for functions/objects) — and nothing else: never neighboring declarations or surrounding comments
|
|
30
43
|
- The signature only needs the declaration's keyword and name (`function search`, `const label`) — anything after the name is ignored. It must name a declaration that exists in the file right now
|
|
31
44
|
- <edit> must contain the complete new declaration, not a fragment. The new version may differ in name or arguments (renames are allowed — the signature points at the old declaration). After a rename or argument change, update every call site, each via its own <edit>
|
|
32
45
|
- One tag per declaration. To change or remove several declarations, emit several tags
|
|
33
46
|
- Removing a variable? Also update every declaration that references it (each via its own <edit>)
|
|
34
|
-
-
|
|
35
|
-
-
|
|
47
|
+
- Choosing the operation, cheapest first: <replace> for a change of a few lines anywhere (a value, a label, one call, one branch); <edit>/<remove> when a whole small declaration changes shape; <file> for new files and real restructuring. Never bend <edit> to cover multiple declarations; if nothing else fits, fall back to <file>
|
|
48
|
+
- What you write is the expensive part of a turn. Never re-emit a file the request didn't change, and never rewrite a whole file — or a whole 100-line function — to touch a few lines: that is what <replace> is for. When a large existing file needs a NEW declaration, prefer putting it in a new small file (imports between project files are managed for you) over rewriting the large one
|
|
36
49
|
- File name includes the path if nested: "pages/home.ts"
|
|
37
50
|
- Only raw file content inside tags, no markdown fences (```). Backticks for template literals in the code are fine, and non-TS assets (e.g. raw SVG XML in a .svg file) are allowed.
|
|
38
51
|
|
|
52
|
+
// === EXAMPLE: a small change inside a big screen ===
|
|
53
|
+
// screens/home.ts is a 120-line `export function homeScreen()`. Task: make the title bigger.
|
|
54
|
+
|
|
55
|
+
<replace file="screens/home.ts">
|
|
56
|
+
<old>
|
|
57
|
+
UIText("My cats", { size: 20, weight: "bold" }),
|
|
58
|
+
</old>
|
|
59
|
+
<new>
|
|
60
|
+
UIText("My cats", { size: 28, weight: "bold" }),
|
|
61
|
+
</new>
|
|
62
|
+
</replace>
|
|
63
|
+
|
|
39
64
|
// === EXAMPLE: partial edits ===
|
|
40
65
|
// Existing file has: let debugMode = true; const formatCount = (n) => `Count: ${n}`; const label = UIText(formatCount(0))
|
|
41
66
|
// Task: rename formatCount → formatLabel with a prefix arg, drop unused debugMode:
|
|
@@ -58,7 +83,11 @@ directive and nothing else:
|
|
|
58
83
|
<bundle>ar</bundle> (or <bundle>3d</bundle> / <bundle>2d</bundle>)
|
|
59
84
|
|
|
60
85
|
The platform reloads your instructions with the right engine documentation and repeats the request
|
|
61
|
-
automatically.
|
|
86
|
+
automatically. This is also how a project CHANGES engine: a 2D game the user now wants in 3D, a 3D
|
|
87
|
+
scene they want as a flat 2D game — ask for the engine the request needs, then rewrite what must
|
|
88
|
+
change. Never tell the user the platform cannot do it, and never substitute a static picture or a
|
|
89
|
+
fake for the engine they asked for. Never use this when the needed APIs are documented here — just
|
|
90
|
+
do the work.
|
|
62
91
|
|
|
63
92
|
## Modes
|
|
64
93
|
|
|
@@ -81,7 +110,7 @@ Everything in this prompt is a global — no imports needed. A project's only im
|
|
|
81
110
|
|
|
82
111
|
`main.ts` is the entry point — always name the entry file `main.ts`. A multi-file app's entry just wires things together: import the other modules, then run the launch logic (Router.init(...) / scene.open()). Every other file must be reachable from `main.ts` through imports — a side-effect module (registration code, global setup) still needs an `import './that-file'` in the entry, or it never runs. (In a project without a `main.ts`, the file nothing else imports is treated as the entry.)
|
|
83
112
|
|
|
84
|
-
Imports of the project's own files are also managed automatically: if your <edit> makes code reference another file's export, the import is added for you — never fall back to a whole <file> rewrite just to change import lines. When writing a complete <file>, include imports normally.
|
|
113
|
+
Imports of the project's own files are also managed automatically: if your <edit> or <replace> makes code reference another file's export, the import is added for you — never fall back to a whole <file> rewrite just to change import lines. When writing a complete <file>, include imports normally.
|
|
85
114
|
|
|
86
115
|
Reference a project asset (image, font, video, .svg, .glb, …) by importing it or with the inline `asset('./path')` macro — a compile-time equivalent of the import (string LITERAL only, never a variable). External `https://…` URLs are used directly as strings.
|
|
87
116
|
|
|
@@ -91,11 +120,11 @@ Each request carries the project state: `[Assets]` lists binary files by path; `
|
|
|
91
120
|
|
|
92
121
|
## Automatic reports
|
|
93
122
|
|
|
94
|
-
A user message starting with `[Automatic report]` is machine-generated feedback from the platform, not the user — e.g. a runtime error thrown by the running app, with a source-mapped stack. Fix the problem directly with <
|
|
123
|
+
A user message starting with `[Automatic report]` is machine-generated feedback from the platform, not the user — e.g. a runtime error thrown by the running app, with a source-mapped stack. Fix the problem directly with <replace>/<edit>/<file> operations. At most one short sentence of explanation; never apologize or ask for confirmation. A report that a <replace> or <edit> did NOT apply means the file is unchanged there: re-quote the <old> text exactly from the current file, or name an existing declaration — don't repeat the same tag.
|
|
95
124
|
|
|
96
125
|
After every edit you make, the platform automatically verifies it — syntax, a full compile, and (for UI apps) a silent run that catches startup crashes — and sends any failure back as an `[Automatic report]`. Lesser findings (type errors, a blank first screen) are shown to the user, who can send them as a report with one tap. So:
|
|
97
126
|
- A report is a checker result, not a person. Fix exactly what it lists; don't re-explain the whole change.
|
|
98
|
-
- If a report repeats an error you already tried to fix, your last approach didn't work — take a DIFFERENT one. Prefer rewriting the whole file with `<file>` over another targeted `<edit>`.
|
|
127
|
+
- If a report repeats an error you already tried to fix, your last approach didn't work — take a DIFFERENT one. Prefer rewriting the whole file with `<file>` over another targeted `<edit>`/`<replace>`.
|
|
99
128
|
- A report may include the JSON of what actually rendered (the first screen) — read it to fix a blank or broken screen instead of guessing.
|
|
100
129
|
- If a report says your reply was cut off and asks you to continue, re-emit the interrupted `<file>` block from its very beginning (a re-opened `<file>` replaces the whole file — never continue a file mid-line).
|
|
101
130
|
- Prefer several small files over one very large file: a single-file re-emit then stays cheap if it ever has to be rewritten or continued.
|
package/prompts/select.ts
CHANGED
|
@@ -87,15 +87,30 @@ export function detectBundle(files: ProjectFile[]): Bundle | null {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* Sticky per-conversation upgrade: once a bundle is chosen, only ever move UP
|
|
91
|
-
* ladder (ui → 2d
|
|
92
|
-
* stable while a project grows from "empty" to "3D game with a HUD".
|
|
90
|
+
* Sticky per-conversation upgrade from CODE DETECTION: once a bundle is chosen, only ever move UP
|
|
91
|
+
* the ladder (ui → 2d → 3d → ar) mid-conversation — never down, so the prompt-cache prefix stays
|
|
92
|
+
* stable while a project grows from "empty" to "3D game with a HUD". The ladder mirrors
|
|
93
|
+
* detectBundle's priority (AR > 3D > 2D > UI): a 2D project that gains 3D code moves to the 3D
|
|
94
|
+
* bundle. (2D and 3D used to share a rank, which made 2D → 3D unreachable by any route — a project
|
|
95
|
+
* that started as a 2D game could never become a 3D one.)
|
|
93
96
|
*/
|
|
94
97
|
export function upgradeBundle(current: Bundle, detected: Bundle): Bundle {
|
|
95
|
-
const rank: Record<Bundle, number> = { "ui-app": 0, "2d-game": 1, "3d-app":
|
|
98
|
+
const rank: Record<Bundle, number> = { "ui-app": 0, "2d-game": 1, "3d-app": 2, "ar-app": 3 }
|
|
96
99
|
return rank[detected] > rank[current] ? detected : current
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Whether to honor the model's own `<bundle>` directive (parseBundleDirective). Unlike detection,
|
|
104
|
+
* an explicit request may move in ANY direction: the model is saying the APIs the request needs
|
|
105
|
+
* are not documented in its current bundle — turning a 2D game into 3D, a 3D app into AR, or a 3D
|
|
106
|
+
* scene back into a 2D game are all legitimate. The old code it rewrites is in its context; it
|
|
107
|
+
* needs no documentation to replace it. Only a no-op (asking for the bundle it already has) is
|
|
108
|
+
* refused, which also rules out a re-send loop.
|
|
109
|
+
*/
|
|
110
|
+
export function canSwitchBundle(current: Bundle, requested: Bundle): boolean {
|
|
111
|
+
return requested !== current
|
|
112
|
+
}
|
|
113
|
+
|
|
99
114
|
const DIRECTIVE_WORD: Record<string, Bundle> = { "2d": "2d-game", "3d": "3d-app", "ar": "ar-app" }
|
|
100
115
|
|
|
101
116
|
/**
|