summer-engine 3.7.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +4 -2
- package/.codex-plugin/plugin.json +5 -3
- package/.cursor-plugin/plugin.json +4 -2
- package/.factory-plugin/plugin.json +4 -2
- package/CHANGELOG.md +16 -0
- package/README.md +11 -1
- package/commands/summer.md +2 -1
- package/dist/core/capabilities/apps.d.ts +318 -0
- package/dist/core/capabilities/apps.js +340 -0
- package/dist/core/capabilities/enable-plugin.d.ts +27 -0
- package/dist/core/capabilities/enable-plugin.js +75 -0
- package/dist/core/capabilities/tool-dispatch.js +49 -0
- package/dist/core/capability-skew.js +4 -0
- package/dist/mcp/hosted-mount.js +13 -1
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/tools/app-tools.d.ts +2 -0
- package/dist/mcp/tools/app-tools.js +64 -0
- package/dist/mcp/tools/generate-tools.js +3 -3
- package/gemini-extension.json +3 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +23 -1
- package/library/skills/README.md +10 -2
- package/library/skills/sound-effect/SKILL.md +2 -0
- package/library/skills/summer-apps/SKILL.md +152 -0
- package/library/skills/summer-apps/manifest.md +57 -0
- package/library/skills/summer-apps/minimal-app.md +183 -0
- package/library/skills/summer-apps/resource.yaml +36 -0
- package/library/skills/summer-apps/sdk-reference.md +135 -0
- package/library/skills/summer-apps-design/SKILL.md +197 -0
- package/library/skills/summer-apps-design/resource.yaml +23 -0
- package/library/skills/using-summer/SKILL.md +1 -0
- package/library/tools/app-call/resource.yaml +59 -0
- package/library/tools/app-create/resource.yaml +67 -0
- package/library/tools/app-data/resource.yaml +74 -0
- package/library/tools/app-dock/resource.yaml +59 -0
- package/library/tools/app-events/resource.yaml +68 -0
- package/library/tools/app-get/resource.yaml +46 -0
- package/library/tools/app-guide/resource.yaml +55 -0
- package/library/tools/app-list/resource.yaml +48 -0
- package/library/tools/app-page/resource.yaml +74 -0
- package/library/tools/app-publish/resource.yaml +90 -0
- package/library/tools/app-report-gap/resource.yaml +58 -0
- package/library/tools/app-screenshot/resource.yaml +85 -0
- package/library/tools/app-tools/resource.yaml +46 -0
- package/library/tools/app-update/resource.yaml +68 -0
- package/library/tools/enable-plugin/resource.yaml +53 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +15 -0
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +3 -1
- package/registry/generated/index.json +656 -3
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +4 -2
- package/registry/generated/plugin.codex.json +5 -3
- package/registry/generated/plugin.cursor.json +4 -2
- package/registry/generated/plugin.factory.json +4 -2
- package/registry/generated/skills-index.md +10 -2
- package/registry/generated/skills-registry.json +27 -0
- package/registry/schemas/allowed-hosts.json +4 -2
|
@@ -106,9 +106,9 @@ async function mcpGenerate(endpoint, body, timeoutMs = 120_000) {
|
|
|
106
106
|
if (!res.ok) {
|
|
107
107
|
let message = formattedApiErrorMessage(res.status, data);
|
|
108
108
|
if (res.status === 402) {
|
|
109
|
-
message
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
// One link: the Add credits step in Studio (no plan needed). Newer servers already put it in the message.
|
|
110
|
+
const base = String(data.message || "Not enough credits.");
|
|
111
|
+
message = base.includes("/studio/credits") ? base : `${base}\nAdd credits: https://summerengine.com/studio/credits`;
|
|
112
112
|
}
|
|
113
113
|
if (res.status === 401) {
|
|
114
114
|
message =
|
package/gemini-extension.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_generated": "GENERATED from integrations/gemini — do not edit; npm run generate:registry",
|
|
3
3
|
"name": "summer",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.8.0",
|
|
5
5
|
"description": "Agent tooling for Summer Engine: MCP bridge, context primer, and game-dev skills. Use with the npm CLI for skill files on disk.",
|
|
6
6
|
"contextFileName": "GEMINI.md",
|
|
7
7
|
"mcpServers": {
|
|
@@ -113,6 +113,8 @@
|
|
|
113
113
|
"./library/skills/store-art/",
|
|
114
114
|
"./library/skills/store-listing/",
|
|
115
115
|
"./library/skills/summer-analytics/",
|
|
116
|
+
"./library/skills/summer-apps/",
|
|
117
|
+
"./library/skills/summer-apps-design/",
|
|
116
118
|
"./library/skills/summer-friends/",
|
|
117
119
|
"./library/skills/summer-leaderboards/",
|
|
118
120
|
"./library/skills/summer-match-results/",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
**Rule of thumb:** project reads/writes go through Summer; live hierarchy/inspector changes use scene tools; process-level work remains with the host.
|
|
19
19
|
|
|
20
|
-
## Tool surface (
|
|
20
|
+
## Tool surface (119 tools)
|
|
21
21
|
|
|
22
22
|
### Project files (4)
|
|
23
23
|
|
|
@@ -262,6 +262,28 @@ Preview. One fast, read-only call that walks every node of a 3D scene and lists
|
|
|
262
262
|
|---|---|
|
|
263
263
|
| `summer_library_feedback` | Report library-entry outcomes (worked/wrong/outdated/...) so entries get fixed and re-ranked. Fire-and-forget with a 1s cap; enum-first schema with no field for project files, chat content, or code; honors `SUMMER_NO_TELEMETRY=1` and `DO_NOT_TRACK=1`. |
|
|
264
264
|
|
|
265
|
+
### Summer Apps (13)
|
|
266
|
+
|
|
267
|
+
Team tools inside Summer Studio that agents build: shared, live on the team's data and engine. Load the `summer-apps` and `summer-apps-design` skills first.
|
|
268
|
+
|
|
269
|
+
| Tool | Use |
|
|
270
|
+
|---|---|
|
|
271
|
+
| `summer_app_guide` | The build guide: when to build, Layout vs code, build loop, manifest, SDK, UI kit, layout format, design rules, worked examples. |
|
|
272
|
+
| `summer_app_list` | The apps in the person's workspaces. |
|
|
273
|
+
| `summer_app_create` | New scratch app; the person's Studio tab opens it. |
|
|
274
|
+
| `summer_app_publish` | New immutable version from a local `dir`, `files` or a `layout`, plus the manifest. |
|
|
275
|
+
| `summer_app_get` | Manifest, current files and a data summary. |
|
|
276
|
+
| `summer_app_update` | Rename, move to a folder, keep, archive or restore an app. |
|
|
277
|
+
| `summer_app_events` | Long-poll (up to 50 s) what the app sends agents, errors, feedback and tool calls. |
|
|
278
|
+
| `summer_app_page` | Read, fill and press the open app's declared fields and buttons. |
|
|
279
|
+
| `summer_app_screenshot` | See the app without the person: a headless, read-only render (PNG) with console, errors, refused calls and the declared page; `width` 390 for a phone, `theme` dark, `out` saves the PNG. |
|
|
280
|
+
| `summer_app_data` | Read or write the app's shared collections; no app needs to be open, the team sees writes live. |
|
|
281
|
+
| `summer_app_tools` | The app's declared agent tools. |
|
|
282
|
+
| `summer_app_call` | Run an agent tool inside the person's open app; `app_not_open` otherwise. |
|
|
283
|
+
| `summer_app_report_gap` | Report an SDK capability an app needed and did not have. |
|
|
284
|
+
| `summer_app_dock` | Dock an app in the editor (left, right, bottom), close it, list docks; `engine_lacks_op` on engines without app docks. |
|
|
285
|
+
| `summer_enable_plugin` | Enable an editor plugin (`res://addons/<name>/`) and get the real load result (`enabled`, autoloads, or a failure `code`); older engines answer `queued: true, enabled: null` instead of claiming success. |
|
|
286
|
+
|
|
265
287
|
## Common pattern
|
|
266
288
|
|
|
267
289
|
Every scene-touching skill should follow this loop:
|
package/library/skills/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Summer skills
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 125 skills, grouped by their primary domain. **Generated** by `npm run generate:registry` from each skill's `resource.yaml`; `--check` fails when this file and the library disagree. Do not edit by hand.
|
|
4
4
|
|
|
5
5
|
The folder is flat on purpose (`docs/design/DECISIONS.md`, D3): a skill usually belongs to several domains, so categories live in each skill's `facets.domains` and this index is rendered from them. The first domain listed is the primary one. `summer skills list --by-domain` prints the same grouping; `summer skills info <slug>` shows one skill.
|
|
6
6
|
|
|
@@ -29,7 +29,8 @@ Status: **stable** unless marked; *preview* = not yet exercised in-engine by the
|
|
|
29
29
|
- [runtime](#runtime) (1)
|
|
30
30
|
- [scenes](#scenes) (12)
|
|
31
31
|
- [scripting](#scripting) (2)
|
|
32
|
-
- [
|
|
32
|
+
- [tools](#tools) (1)
|
|
33
|
+
- [ui](#ui) (2)
|
|
33
34
|
- [vfx](#vfx) (9)
|
|
34
35
|
- [video](#video) (3)
|
|
35
36
|
|
|
@@ -248,10 +249,17 @@ Status: **stable** unless marked; *preview* = not yet exercised in-engine by the
|
|
|
248
249
|
| [gdscript-patterns](./gdscript-patterns/SKILL.md) ★ | GDScript conventions — type hints, signals, exports, onready, lifecycle methods, get_node vs $NodePath, naming. | conventions |
|
|
249
250
|
| [*save-load*](./save-load/SKILL.md) (preview) | Persist game state in Summer Engine: user:// paths, JSON progress vs ConfigFile settings, a versioned save schema with migration, autosave hooks. | gameplay |
|
|
250
251
|
|
|
252
|
+
## tools
|
|
253
|
+
|
|
254
|
+
| skill | when to use | also |
|
|
255
|
+
|---|---|---|
|
|
256
|
+
| [*summer-apps*](./summer-apps/SKILL.md) ★ (preview) | Build a Summer App, a team tool inside Summer Studio (balance table, quest editor, tracker, pick grid): build loop, Layout vs code, data as API, agent tools. | ui, agent-workflow |
|
|
257
|
+
|
|
251
258
|
## ui
|
|
252
259
|
|
|
253
260
|
| skill | when to use | also |
|
|
254
261
|
|---|---|---|
|
|
262
|
+
| [*summer-apps-design*](./summer-apps-design/SKILL.md) ★ (preview) | How a Summer App should look - page layout, Summer UI kit components, tables, charts, media, boards, states and a pre-ship checklist. | tools |
|
|
255
263
|
| [ui-basics](./ui-basics/SKILL.md) ★ | Build Summer Engine UI — HUDs, menus, health bars, dialogue boxes, Control trees — anchors, containers, responsive layout, theme vs inline styling. | ux |
|
|
256
264
|
|
|
257
265
|
## vfx
|
|
@@ -106,6 +106,8 @@ summer_generate_audio(capability: "sound_effects", text: "<prompt>", durationSec
|
|
|
106
106
|
|
|
107
107
|
Leave out `modelId` so their approved default runs. The description goes in `text`, not `prompt` (`prompt` gives a 400 `text_required` here).
|
|
108
108
|
|
|
109
|
+
No Summer project (an asset-only request, or the engine is not installed or not running): stop here. The clips are in the person's Summer library; give them the `audioUrl` of each approved version and skip steps 7 and 8.
|
|
110
|
+
|
|
109
111
|
### 7. Import
|
|
110
112
|
|
|
111
113
|
```
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summer-apps
|
|
3
|
+
description: "Build a Summer App, a team tool inside Summer Studio (balance table, quest editor, tracker, pick grid): build loop, Layout vs code, data as API, agent tools."
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: tools
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Write Edit Glob summer_app_guide summer_app_list summer_app_create summer_app_publish summer_app_get summer_app_update summer_app_events summer_app_page summer_app_screenshot summer_app_data summer_app_tools summer_app_call summer_app_dock summer_app_report_gap
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Summer Apps
|
|
12
|
+
|
|
13
|
+
A Summer App is a tool the person's team uses inside Summer Studio: shared with
|
|
14
|
+
the workspace, live on its assets, game data and engine, dockable in the editor.
|
|
15
|
+
You build it in minutes; the team keeps it.
|
|
16
|
+
|
|
17
|
+
## First: does Summer already do it?
|
|
18
|
+
|
|
19
|
+
A custom app is something the team has to maintain. Before you build one, check whether
|
|
20
|
+
Summer already does the job: Studio features (the asset library with its collections and
|
|
21
|
+
groups, Pick one and sound choices, Grow dashboards, Team) and the existing `summer_*` MCP
|
|
22
|
+
tools. If it does, use that and tell the person where it lives.
|
|
23
|
+
|
|
24
|
+
Build an app only when the need is real and Summer lacks it, or as a Scratch throwaway (a
|
|
25
|
+
quick asset review, a UI prototype) that you call throwaway when you make it, and archive
|
|
26
|
+
when it is done (`summer_app_update {appId, status: "archived"}`). When
|
|
27
|
+
something is missing natively, report it with `summer_app_report_gap` (and
|
|
28
|
+
`summer_library_feedback` when a library entry fell short), so Summer can build it for
|
|
29
|
+
everyone.
|
|
30
|
+
|
|
31
|
+
| Ask | Do |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| "Let me pick from these 12 sword sounds" | Pick one: `summer_generate_sfx_choices`, or put the sounds in a library collection. No app. |
|
|
34
|
+
| "Group our Studio assets into a moodboard for the forest level" | Organize them with the asset library's collections and groups. No app. |
|
|
35
|
+
| "A weapon balance table that writes res://data/weapons.json" | Build an app: nothing in Summer edits game data with the team's balance rules. |
|
|
36
|
+
|
|
37
|
+
## When to build one
|
|
38
|
+
|
|
39
|
+
- The person asks for a tool ("build us a loot balance tool", "track every creature
|
|
40
|
+
from concept to in-engine", "a board to review playtest clips").
|
|
41
|
+
- They need a UI to decide or edit something they will come back to.
|
|
42
|
+
- Not for a one-off answer: reply in chat instead.
|
|
43
|
+
- Not when Summer already does it (above).
|
|
44
|
+
|
|
45
|
+
## Layout or code
|
|
46
|
+
|
|
47
|
+
- **Layout** (`format: "layout"`): a JSON layout of kit nodes. Quick, throwaway,
|
|
48
|
+
always on brand. Use it to ask the person to pick, rate or fill something now.
|
|
49
|
+
- **Code** (default): `index.html` + JS/CSS with the Summer SDK and UI kit. Use it
|
|
50
|
+
for state, editing, shared data, generation or engine access.
|
|
51
|
+
|
|
52
|
+
## Start from the working app
|
|
53
|
+
|
|
54
|
+
Read these before writing code; they are exact:
|
|
55
|
+
|
|
56
|
+
- [minimal-app.md](minimal-app.md): a complete working app (manifest, `index.html`,
|
|
57
|
+
`app.js`) built from kit components, using shared data, live changes, the viewer, `page.declare`, an
|
|
58
|
+
agent event, an agent tool and frame-move state. Copy it and change it.
|
|
59
|
+
- [sdk-reference.md](sdk-reference.md): every SDK call with its exact arguments and
|
|
60
|
+
return shape.
|
|
61
|
+
- [manifest.md](manifest.md): every manifest field.
|
|
62
|
+
- The kit's component catalog (every `<summer-*>` element with attributes, events and a
|
|
63
|
+
copyable example) is `summer_app_guide` section `kit`, also at `/summer-ui/v1/catalog.json`
|
|
64
|
+
on the app host. Copy its examples instead of inventing markup.
|
|
65
|
+
|
|
66
|
+
To see an app, publish it and call `summer_app_screenshot {appId}`: Summer renders it headless
|
|
67
|
+
with the person's data and returns the picture plus the console, uncaught errors, refused calls
|
|
68
|
+
and the fields and buttons it declared. No Studio tab needed; the person sees it at
|
|
69
|
+
`/studio/apps/<appId>` (`summer_app_create` opens it for them). Opening `index.html` on its own
|
|
70
|
+
never connects. There is no offline host: do not build one or mock the SDK.
|
|
71
|
+
|
|
72
|
+
The app is served from its own host, apps.summerengine.app. Load the SDK and the kit as
|
|
73
|
+
ES modules with root paths: `<script type="module" src="/summer-sdk/v1.js">` and
|
|
74
|
+
`<script type="module" src="/summer-ui/v1/ui.js">`. Then `await summer.ready` before
|
|
75
|
+
the first call, and reach data only through `summer.data.collection(name)`.
|
|
76
|
+
|
|
77
|
+
## The loop
|
|
78
|
+
|
|
79
|
+
1. `summer_app_guide` (whole once per session; later one `section`). Load the
|
|
80
|
+
`summer-apps-design` skill for how it must look.
|
|
81
|
+
2. `summer_app_create {name, purpose, format}`. The person's Studio tab opens it.
|
|
82
|
+
3. Write the files in a local folder, then `summer_app_publish {appId, dir, manifest, note}`
|
|
83
|
+
(or `layout`). Each publish is an immutable version the tab boots. A publish
|
|
84
|
+
error names the file or rule: fix it and publish again.
|
|
85
|
+
4. Seed starter data with `summer_app_data` (`ops` plus `"as": "seed"`) when the app should
|
|
86
|
+
start full. Seed rows belong to the team and show no author.
|
|
87
|
+
5. Look at it: `summer_app_screenshot {appId}`, then `width: 390` (phone) and `theme: "dark"`.
|
|
88
|
+
Fix every error and console line it reports, publish again, look again. It is read-only:
|
|
89
|
+
writes, sends and paid calls are refused there and work in Studio.
|
|
90
|
+
6. `summer_app_events {appId, after}` waits for what the app sends you
|
|
91
|
+
(`summer.agent.send`), errors (uncaught and `console.error`) and feedback. Act on them;
|
|
92
|
+
pass the cursor on.
|
|
93
|
+
7. Change the app by publishing again; `summer_app_get` returns the current files.
|
|
94
|
+
|
|
95
|
+
## Data is the API
|
|
96
|
+
|
|
97
|
+
Read and write an app's shared collections with `summer_app_data`. It works with
|
|
98
|
+
no app open, and everyone in the app sees your edits live. "Nerf the bow in our
|
|
99
|
+
balance sheet" is a data write, not a click.
|
|
100
|
+
|
|
101
|
+
Declare each collection's record shape in the manifest `collections`; `summer_app_tools`
|
|
102
|
+
and `summer_app_data` reads return it, so any agent knows what a record looks like.
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{ "appId": "<id>", "ops": [
|
|
106
|
+
{ "op": "patch", "collection": "items", "recordId": "bow", "patch": { "damage": 36 }, "ifRevision": 4 },
|
|
107
|
+
{ "op": "put", "collection": "notes", "recordId": "n-17", "data": { "text": "Boss too long", "status": "open" } },
|
|
108
|
+
{ "op": "remove", "collection": "notes", "recordId": "n-3" }
|
|
109
|
+
] }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Up to 100 ops per call. Without `ops`, `summer_app_data` reads (`collection`, `after`, `limit`);
|
|
113
|
+
removed records are not returned. Your writes are recorded as `actor { kind: "agent" }` (and
|
|
114
|
+
as `createdBy` on records you create), so the app can show which rows an agent made. Add
|
|
115
|
+
`"as": "seed"` for starter data that belongs to the team (`kind: "seed"`, no author). `patch` merges keys
|
|
116
|
+
(`null` removes one); `ifRevision` guards against overwriting a newer change.
|
|
117
|
+
|
|
118
|
+
## Agent tools
|
|
119
|
+
|
|
120
|
+
Logic that lives in the app (a balance formula, an export) can be exposed in the
|
|
121
|
+
manifest `agentTools` and implemented with `summer.agent.tool(name, handler)`.
|
|
122
|
+
`summer_app_tools` lists them; `summer_app_call` runs one inside the person's open
|
|
123
|
+
app and answers `app_not_open` otherwise. Prefer data for plain reads and edits.
|
|
124
|
+
|
|
125
|
+
## Rules
|
|
126
|
+
|
|
127
|
+
- Declare only the permissions the app uses; declare every `engineFiles` path and
|
|
128
|
+
every `agentEvents` name. A missing one fails at runtime with `permission_missing`.
|
|
129
|
+
- The app runs sandboxed (`allow-scripts allow-forms allow-popups allow-modals
|
|
130
|
+
allow-downloads`, never `allow-same-origin`): no cookies, no `localStorage`, no network
|
|
131
|
+
unless the workspace allows a domain. Keep state in `summer.data`.
|
|
132
|
+
- Paid SDK calls spend the credits of the person using the app. Say so before
|
|
133
|
+
building an app that generates.
|
|
134
|
+
- Use real data (`summer.data`, `summer.assets`, `summer.engine.readFile`), never
|
|
135
|
+
placeholder numbers.
|
|
136
|
+
- Studio moves an idle app to a fresh frame every few minutes. Keep UI state you care
|
|
137
|
+
about in shared data, or return it from `summer.app.onSnapshot(() => state)` (up to
|
|
138
|
+
256 KB) and read it back from `(await summer.ready).restored`. Wrap Web Audio playback
|
|
139
|
+
in `summer.app.busy(true)` / `summer.app.busy(false)` so a long sound is not cut;
|
|
140
|
+
`<audio>` and `<video>` count on their own.
|
|
141
|
+
- Call `summer.page.declare` so agents can read and use the UI; mark delete,
|
|
142
|
+
publish, pay and send actions `critical`.
|
|
143
|
+
- When the SDK cannot do what the app needs, say so and call
|
|
144
|
+
`summer_app_report_gap`.
|
|
145
|
+
- `summer_app_dock` docks an app in the editor; on engines without app docks it
|
|
146
|
+
returns `engine_lacks_op`: open the app in the Studio tab instead.
|
|
147
|
+
|
|
148
|
+
## Done means
|
|
149
|
+
|
|
150
|
+
The app is published; `summer_app_screenshot` shows it with real data in light, dark and at
|
|
151
|
+
390 px with no errors; its states (loading, empty, error) work; and the person knows what it
|
|
152
|
+
does and where its data goes.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# The Summer App manifest
|
|
2
|
+
|
|
3
|
+
Sent as `manifest` on every `summer_app_publish` and written into the app as
|
|
4
|
+
`summer.app.json`. Unknown keys and unknown permissions are refused with a message that
|
|
5
|
+
names them. Every field:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"name": "Loot table",
|
|
10
|
+
"purpose": "Balance item stats and write them to res://data/items.json",
|
|
11
|
+
"permissions": ["data", "assets:read", "generate", "ai", "team:read", "engine:read", "engine:write"],
|
|
12
|
+
"engineFiles": ["res://data/items.json"],
|
|
13
|
+
"collections": {
|
|
14
|
+
"items": {
|
|
15
|
+
"description": "One weapon or armour piece",
|
|
16
|
+
"fields": {
|
|
17
|
+
"name": { "type": "string", "required": true },
|
|
18
|
+
"damage": { "type": "number", "description": "Damage per hit" },
|
|
19
|
+
"speed": { "type": "number", "description": "Attacks per second" },
|
|
20
|
+
"tags": { "type": "array" }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"agentEvents": ["items.written"],
|
|
25
|
+
"agentTools": [
|
|
26
|
+
{
|
|
27
|
+
"name": "nerf_item",
|
|
28
|
+
"description": "Lower one item's damage by a percent",
|
|
29
|
+
"input": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": { "id": { "type": "string" }, "percent": { "type": "number" } },
|
|
32
|
+
"required": ["id", "percent"]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"surface": "both",
|
|
37
|
+
"dock": { "preferred": "bottom" },
|
|
38
|
+
"plugin": { "name": "loot_tools" }
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| Key | Rules |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `name` | Required, 1-80 characters. |
|
|
45
|
+
| `purpose` | Up to 280 characters: what the tool does and where its data goes. |
|
|
46
|
+
| `permissions` | Only these: `data`, `assets:read`, `assets:write`, `generate`, `ai`, `projects:read`, `team:read`, `grow:read`, `engine:read`, `engine:write`, `choices`. `engine:write` includes `engine:read`; `assets:write` includes `assets:read`. No permission is needed for `app.*`, `agent.*`, `page.*` and `team.presence`. |
|
|
47
|
+
| `engineFiles` | Up to 50 `res://` paths `engine.readFile` / `writeFile` may touch. |
|
|
48
|
+
| `collections` | Up to 50, keyed by collection name (lowercase `a-z 0-9 _ . -`). Each: `description`, `fields` (up to 50, name `[A-Za-z_][A-Za-z0-9_]*`) with `type` (`string`, `number`, `integer`, `boolean`, `object`, `array`, `any`), `description`, `required`. Descriptive: agents and people read it; writes are not rejected for it. |
|
|
49
|
+
| `agentEvents` | Up to 50 names (`a-z 0-9 _ . -`) the app sends with `summer.agent.send`. |
|
|
50
|
+
| `agentTools` | Up to 20: `name` (`a-z 0-9 _`, max 64), `description` (max 1,000), `input` (JSON Schema, up to 8 KB). |
|
|
51
|
+
| `surface` | `page` (the Studio app page, default), `dock` (an editor dock) or `both`. A dock layout must work from 320 px wide; docked apps get selection and scene events (`summer.engine.onEvent`). |
|
|
52
|
+
| `dock` | `{ "preferred": "left" \| "right" \| "bottom" }`. |
|
|
53
|
+
| `plugin` | `{ "name": "<folder>" }`: the app may also write `res://addons/<folder>/`. |
|
|
54
|
+
| `entry` | Always `index.html` (you can leave it out). |
|
|
55
|
+
|
|
56
|
+
The whole manifest is at most 200 KB. A code app's bundle: `index.html` at the root, at most
|
|
57
|
+
300 files, 10 MB, 5 MB per file.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# A complete minimal Summer App
|
|
2
|
+
|
|
3
|
+
A working code app: shared notes for a playtest. Copy it, rename things, then grow it.
|
|
4
|
+
It shows every piece an app needs: the manifest, loading the SDK and kit, waiting for
|
|
5
|
+
the host, shared data (list, put, live changes), who changed a row (person or agent),
|
|
6
|
+
an edit flow (dialog + form with setValues), status badges, an error with Try again, the
|
|
7
|
+
page declaration, an agent event, an agent tool and frame-move state.
|
|
8
|
+
|
|
9
|
+
Publish it with `summer_app_publish {appId, dir, manifest, note}` where `dir` holds
|
|
10
|
+
`index.html` and `app.js`, or pass the same two as `files`. The page, sections, inputs,
|
|
11
|
+
table, notice and empty text are kit components (`summer_app_guide` section `kit`, or
|
|
12
|
+
`/summer-ui/v1/catalog.json` on the app host); no app CSS is needed.
|
|
13
|
+
|
|
14
|
+
## The manifest (send it as `manifest`)
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"name": "Playtest notes",
|
|
19
|
+
"purpose": "Tester notes per level. Notes live in this app's shared data.",
|
|
20
|
+
"permissions": ["data"],
|
|
21
|
+
"collections": {
|
|
22
|
+
"notes": {
|
|
23
|
+
"description": "One remark from a playtest",
|
|
24
|
+
"fields": {
|
|
25
|
+
"text": { "type": "string", "required": true, "description": "What the tester said" },
|
|
26
|
+
"level": { "type": "string", "description": "Level name, e.g. bridge" },
|
|
27
|
+
"status": { "type": "string", "description": "open or fixed" },
|
|
28
|
+
"author": { "type": "string", "description": "Who added it" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"agentEvents": ["note.added"],
|
|
33
|
+
"agentTools": [
|
|
34
|
+
{
|
|
35
|
+
"name": "add_note",
|
|
36
|
+
"description": "Add a playtest note",
|
|
37
|
+
"input": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": { "text": { "type": "string" }, "level": { "type": "string" } },
|
|
40
|
+
"required": ["text"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## index.html
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<!doctype html>
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="utf-8">
|
|
54
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
55
|
+
<title>Playtest notes</title>
|
|
56
|
+
<!-- Both are ES modules on the app's own host (apps.summerengine.app). Root paths, type="module". -->
|
|
57
|
+
<script type="module" src="/summer-sdk/v1.js"></script>
|
|
58
|
+
<script type="module" src="/summer-ui/v1/ui.js"></script>
|
|
59
|
+
</head>
|
|
60
|
+
<body>
|
|
61
|
+
<summer-page heading="Playtest notes" purpose="Tester notes per level, shared with your team and your agent.">
|
|
62
|
+
<summer-button slot="actions" id="add" variant="primary" icon="plus">Add note</summer-button>
|
|
63
|
+
<summer-section heading="Notes">
|
|
64
|
+
<summer-notice id="error" tone="error" action="Try again" action-icon="refresh-cw" hidden></summer-notice>
|
|
65
|
+
<summer-table id="table" row-key="id" sortable clickable loading
|
|
66
|
+
empty-text="No notes yet. Add the first one, or ask your agent to add the testers' notes."></summer-table>
|
|
67
|
+
</summer-section>
|
|
68
|
+
<summer-dialog id="dialog" heading="Note" description="Shared with your team and your agent.">
|
|
69
|
+
<summer-form id="form" submit-label="Save note">
|
|
70
|
+
<summer-text-input label="Note" name="text" multiline required></summer-text-input>
|
|
71
|
+
<summer-grid columns="2">
|
|
72
|
+
<summer-text-input label="Level" name="level" placeholder="bridge"></summer-text-input>
|
|
73
|
+
<summer-select label="Status" name="status" options='[{"value":"open","label":"Open"},{"value":"fixed","label":"Fixed"}]'></summer-select>
|
|
74
|
+
</summer-grid>
|
|
75
|
+
</summer-form>
|
|
76
|
+
</summer-dialog>
|
|
77
|
+
</summer-page>
|
|
78
|
+
<!-- app.js runs after the SDK module (module scripts run in order). -->
|
|
79
|
+
<script type="module" src="app.js"></script>
|
|
80
|
+
</body>
|
|
81
|
+
</html>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## app.js
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
const notes = summer.data.collection("notes");
|
|
88
|
+
const info = await summer.ready; // { viewer: { userId, name }, params, restored, ... }
|
|
89
|
+
const $ = (id) => document.getElementById(id);
|
|
90
|
+
const table = $("table"), dialog = $("dialog"), form = $("form"), error = $("error");
|
|
91
|
+
let editing = null; // the note id being edited, or null for a new one
|
|
92
|
+
|
|
93
|
+
table.columns = [
|
|
94
|
+
{ key: "text", label: "Note", priority: "primary" },
|
|
95
|
+
{ key: "level", label: "Level" },
|
|
96
|
+
{ key: "status", label: "Status", badges: { open: "warn", fixed: "ok" } }, // one badge tone per value
|
|
97
|
+
{ key: "by", label: "By", type: "muted" },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
async function load() {
|
|
101
|
+
table.setAttribute("loading", "");
|
|
102
|
+
try {
|
|
103
|
+
const page = await notes.list({ limit: 500 }); // removed notes are never listed
|
|
104
|
+
table.rows = page.records.map((r) => ({
|
|
105
|
+
id: r.recordId,
|
|
106
|
+
...r.data,
|
|
107
|
+
// createdBy: who made the row ({ userId, kind: "person" | "agent" | "seed", agent?, via? });
|
|
108
|
+
// later edits do not change it (actor is the last writer). Seed rows have no author.
|
|
109
|
+
by: r.createdBy?.kind === "agent" ? `${r.createdBy.agent ?? "Agent"} (agent)` : r.data.author ?? "",
|
|
110
|
+
}));
|
|
111
|
+
error.hidden = true;
|
|
112
|
+
} catch (e) {
|
|
113
|
+
error.setAttribute("text", `Could not load notes: ${e.message}`);
|
|
114
|
+
error.hidden = false;
|
|
115
|
+
} finally {
|
|
116
|
+
table.removeAttribute("loading");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
error.addEventListener("action", load); // the notice's "Try again" button
|
|
120
|
+
|
|
121
|
+
function openEditor(row) {
|
|
122
|
+
editing = row?.id ?? null;
|
|
123
|
+
form.setValues(row ?? { text: "", level: "", status: "open" });
|
|
124
|
+
dialog.show();
|
|
125
|
+
}
|
|
126
|
+
$("add").addEventListener("click", () => openEditor(null));
|
|
127
|
+
table.addEventListener("summer-row", (event) => openEditor(event.detail.row)); // click a row to edit
|
|
128
|
+
|
|
129
|
+
form.addEventListener("submit", async (event) => {
|
|
130
|
+
const { text, level, status } = event.detail.values; // numbers come as numbers, empty as null
|
|
131
|
+
form.setAttribute("busy", "");
|
|
132
|
+
try {
|
|
133
|
+
const id = editing ?? crypto.randomUUID();
|
|
134
|
+
await notes.put(id, { text, level: level ?? "", status: status ?? "open", author: info.viewer.name ?? "someone" });
|
|
135
|
+
if (!editing) await summer.agent.send("note.added", { id, text, level }); // name is in manifest.agentEvents
|
|
136
|
+
dialog.close();
|
|
137
|
+
summerUI.toast("Note saved");
|
|
138
|
+
} catch (e) {
|
|
139
|
+
form.setAttribute("error", e.message);
|
|
140
|
+
} finally {
|
|
141
|
+
form.removeAttribute("busy");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Live: every change by a teammate or an agent arrives as { revision, actor, ops } (or { reset: true }).
|
|
146
|
+
notes.subscribe(() => load());
|
|
147
|
+
|
|
148
|
+
// Agent tool: summer_app_call { tool: "add_note", input: { text, level } } runs this while the app is open.
|
|
149
|
+
// Its writes are recorded as the calling agent's (actor.kind "agent", via "agent-tool").
|
|
150
|
+
summer.agent.tool("add_note", async ({ text, level }, { agent }) => {
|
|
151
|
+
const id = crypto.randomUUID();
|
|
152
|
+
await notes.put(id, { text, level: level ?? "", status: "open", author: agent });
|
|
153
|
+
return { id };
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// What agents can read and use with summer_app_page.
|
|
157
|
+
summer.page.declare({ actions: [{ id: "add", label: "Add note", run: () => openEditor(null) }] });
|
|
158
|
+
|
|
159
|
+
// Frame moves: Studio moves an idle app to a fresh frame every few minutes. Shared data survives on
|
|
160
|
+
// its own; keep only UI state, such as an open editor.
|
|
161
|
+
summer.app.onSnapshot(() => (dialog.hasAttribute("open") ? { editing, values: form.values } : null));
|
|
162
|
+
if (info.restored?.values) {
|
|
163
|
+
editing = info.restored.editing;
|
|
164
|
+
form.setValues(info.restored.values);
|
|
165
|
+
dialog.show();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
load();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Playing sound with Web Audio instead of `<audio>`? Wrap it so the frame is not moved
|
|
172
|
+
mid-sound: `summer.app.busy(true)` when it starts, `summer.app.busy(false)` in the
|
|
173
|
+
source's `onended`.
|
|
174
|
+
|
|
175
|
+
## The agent side of the same app
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{ "appId": "<id>", "ops": [{ "op": "put", "collection": "notes", "recordId": "n-1", "data": { "text": "Boss fight too long", "level": "boss", "status": "open", "author": "agent" } }] }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
That is a `summer_app_data` write: it works with the app closed, and the open app shows it
|
|
182
|
+
through `notes.subscribe`. `summer_app_tools` and `summer_app_data` reads return the
|
|
183
|
+
`collections` above, so an agent knows the record shape.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
id: skill/summer-apps
|
|
2
|
+
kind: skill
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
summary: "Build a Summer App, a team tool inside Summer Studio (balance table, quest editor, tracker, pick grid): build loop, Layout vs code, data as API, agent tools."
|
|
5
|
+
use_when:
|
|
6
|
+
- the person asks for a tool for their team ("build us a loot balance tool", "a board to track creatures")
|
|
7
|
+
- the person needs a UI to decide, rate or edit something they will come back to
|
|
8
|
+
- reading or changing the data of an existing Summer App
|
|
9
|
+
- calling an app's agent tools or waiting for what an app sends
|
|
10
|
+
facets:
|
|
11
|
+
lifecycle:
|
|
12
|
+
- build
|
|
13
|
+
domains:
|
|
14
|
+
- tools
|
|
15
|
+
- ui
|
|
16
|
+
- agent-workflow
|
|
17
|
+
modalities:
|
|
18
|
+
- ui
|
|
19
|
+
- docs
|
|
20
|
+
compatibility:
|
|
21
|
+
engine: ">=4.6"
|
|
22
|
+
related:
|
|
23
|
+
skills:
|
|
24
|
+
- skill/summer-apps-design
|
|
25
|
+
tools:
|
|
26
|
+
- tool/app-guide
|
|
27
|
+
- tool/app-create
|
|
28
|
+
- tool/app-publish
|
|
29
|
+
- tool/app-events
|
|
30
|
+
- tool/app-data
|
|
31
|
+
- tool/app-call
|
|
32
|
+
- tool/app-dock
|
|
33
|
+
source: official
|
|
34
|
+
license: MIT
|
|
35
|
+
status: preview
|
|
36
|
+
recommended: true
|