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.
Files changed (59) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +4 -2
  3. package/.codex-plugin/plugin.json +5 -3
  4. package/.cursor-plugin/plugin.json +4 -2
  5. package/.factory-plugin/plugin.json +4 -2
  6. package/CHANGELOG.md +16 -0
  7. package/README.md +11 -1
  8. package/commands/summer.md +2 -1
  9. package/dist/core/capabilities/apps.d.ts +318 -0
  10. package/dist/core/capabilities/apps.js +340 -0
  11. package/dist/core/capabilities/enable-plugin.d.ts +27 -0
  12. package/dist/core/capabilities/enable-plugin.js +75 -0
  13. package/dist/core/capabilities/tool-dispatch.js +49 -0
  14. package/dist/core/capability-skew.js +4 -0
  15. package/dist/mcp/hosted-mount.js +13 -1
  16. package/dist/mcp/server.js +2 -0
  17. package/dist/mcp/tools/app-tools.d.ts +2 -0
  18. package/dist/mcp/tools/app-tools.js +64 -0
  19. package/dist/mcp/tools/generate-tools.js +3 -3
  20. package/gemini-extension.json +3 -1
  21. package/library/references/mcp-tools-reference/mcp-tools-reference.md +23 -1
  22. package/library/skills/README.md +10 -2
  23. package/library/skills/sound-effect/SKILL.md +2 -0
  24. package/library/skills/summer-apps/SKILL.md +152 -0
  25. package/library/skills/summer-apps/manifest.md +57 -0
  26. package/library/skills/summer-apps/minimal-app.md +183 -0
  27. package/library/skills/summer-apps/resource.yaml +36 -0
  28. package/library/skills/summer-apps/sdk-reference.md +135 -0
  29. package/library/skills/summer-apps-design/SKILL.md +197 -0
  30. package/library/skills/summer-apps-design/resource.yaml +23 -0
  31. package/library/skills/using-summer/SKILL.md +1 -0
  32. package/library/tools/app-call/resource.yaml +59 -0
  33. package/library/tools/app-create/resource.yaml +67 -0
  34. package/library/tools/app-data/resource.yaml +74 -0
  35. package/library/tools/app-dock/resource.yaml +59 -0
  36. package/library/tools/app-events/resource.yaml +68 -0
  37. package/library/tools/app-get/resource.yaml +46 -0
  38. package/library/tools/app-guide/resource.yaml +55 -0
  39. package/library/tools/app-list/resource.yaml +48 -0
  40. package/library/tools/app-page/resource.yaml +74 -0
  41. package/library/tools/app-publish/resource.yaml +90 -0
  42. package/library/tools/app-report-gap/resource.yaml +58 -0
  43. package/library/tools/app-screenshot/resource.yaml +85 -0
  44. package/library/tools/app-tools/resource.yaml +46 -0
  45. package/library/tools/app-update/resource.yaml +68 -0
  46. package/library/tools/enable-plugin/resource.yaml +53 -0
  47. package/package.json +1 -1
  48. package/registry/generated/aliases.json +15 -0
  49. package/registry/generated/counts.json +3 -3
  50. package/registry/generated/gemini-extension.json +3 -1
  51. package/registry/generated/index.json +656 -3
  52. package/registry/generated/marketplace.claude.json +2 -2
  53. package/registry/generated/plugin.claude.json +4 -2
  54. package/registry/generated/plugin.codex.json +5 -3
  55. package/registry/generated/plugin.cursor.json +4 -2
  56. package/registry/generated/plugin.factory.json +4 -2
  57. package/registry/generated/skills-index.md +10 -2
  58. package/registry/generated/skills-registry.json +27 -0
  59. 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
- (data.message || "Insufficient credits.") +
111
- "\nTop up at: https://www.summerengine.com/dashboard\nOr upgrade your plan at: https://www.summerengine.com/pricing";
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 =
@@ -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.7.0",
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 (104 tools)
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:
@@ -1,6 +1,6 @@
1
1
  # Summer skills
2
2
 
3
- > 123 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.
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
- - [ui](#ui) (1)
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