Files
feuerwerk/src/editor/agent/tools.json
T
adminandClaude Opus 5.5 56425fca6a
feuerwerk / gate (push) Successful in 5m22s
feuerwerk / suite (push) Failing after 19m54s
Let a song carry named markers on a strip in the song tab (#338)
Stage 1 of #338. Game sync needs named places in a song: an event when
the playhead passes one, and a switch that jumps to one. This stage
adds the markers themselves. Games do nothing with them yet.

- A song has markers, each with id, name and tick. A song without
  markers stores nothing for them. Two markers may share tick and
  name, since nodes will name them by id.
- Markers change neither the sound nor the bake fingerprint.
- The song tab draws a strip under the ruler. A marker has a green
  flag with its name and a faint line through the tracks. Tracks and
  lanes sit 18 px lower. The ruler of the pattern tab is unchanged.
- Double click on an empty place adds a marker on the grid, with Alt
  or Shift on the exact tick. Dragging moves it in one undo step.
- A click on a marker sets the start of playback there. A double click
  renames it in the name dialog of the hierarchy groups.
- The context menu offers Play from here, Rename and Delete marker,
  on an empty place Add marker. A tooltip shows the full name. The
  wheel over the strip zooms time like the ruler.
- New agent commands add_song_marker, change_song_marker and
  remove_song_marker. get_asset lists them under markers.

Tests:
- song-markers in core 6 and editor 4, 1 agent test, with the touched
  song, agent and MCP tests 175/175
- 9 counter-probes, all compile, all red
- e2e 65 on atx: ALL PASSED, 70 checks, 3 new in 65g3. Counter-probe
  where a dragged marker is not taken over: 65g3 red
- gate on atx: CHECK EXIT 0, 332 files, 5093 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:44:58 +02:00

292 lines
17 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[
{
"name": "get_view",
"title": "What the user sees",
"description": "What the user is looking at in the editor right now: the project, the open tab and document, the selection (asset, scene objects, graph nodes, keys, ...), the camera of the view showing, whether the game is playing or paused, and an open menu or dialog. Call it first, and again whenever you were told the view changed.",
"inputSchema": { "type": "object", "properties": {}, "additionalProperties": false },
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "screenshot",
"title": "Screenshot of the editor",
"description": "The whole editor as the user sees it, as a PNG: toolbar, panels, the open tab, menus and dialogs included. Scaled down to maxWidth pixels (default 1280) if it is wider.",
"inputSchema": {
"type": "object",
"properties": {
"maxWidth": {
"type": "integer",
"minimum": 320,
"maximum": 3840,
"description": "Widest the picture may be, in pixels."
}
},
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "list_assets",
"title": "List the assets",
"description": "The project's settings (start scene, layers, master bus, ...) and its assets with their ids, names and types, and how many objects or nodes each holds. Filter the assets by type and by a part of the name.",
"inputSchema": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Only assets of this type (scene, sprite, behavior, ...)."
},
"query": { "type": "string", "description": "Only assets whose name contains this." }
},
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "list_files",
"title": "List the stored files",
"description": "The pictures, typefaces, sounds or videos the server holds for this account, the demo material among them (demo), whether a project uses them or not: each with its file, the name it was uploaded as, its size, a picture's width and height, a video's state, length, frame rate and sound, and the asset already wearing it in this project (asset). The command add_file_asset makes an asset of one; uploading is for the user.",
"inputSchema": {
"type": "object",
"properties": {
"type": { "type": "string", "enum": ["texture", "font", "sound", "video"] },
"query": { "type": "string", "description": "Only files whose upload name contains this." }
},
"required": ["type"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "get_asset",
"title": "Read an asset",
"description": "One asset as stored: its property values, the rows of its properties (key, kind, default), the objects of a scene or container with their names and positions (the content of a placed container too, marked fromContainer), what the rows of an animation may aim at (targets), the cells of a small tilemap, the pixels of small pixel art as rows of palette indices (colors as \"#rrggbb\"), the notes of a pattern with their names and how many ticks it is long, on each track of a song its automation lanes and the parameters a lane can move (params), the markers of a song with their ids, names and ticks, the rows a clip of a sound or video has (clipProperties), how many seconds a loaded sound lasts, and under silent what of a pattern, song or composition is not heard: all of it without an instrument, or the notes its instrument does not sound. Under usedBy: every asset naming it or one of its frames or clips, with the objects, nodes, items or keys in there that do (none for a property of the asset itself), and the project's start scene, master FX or Master routing; empty when nothing uses it. A graph is only counted here; read it with get_graph.",
"inputSchema": {
"type": "object",
"properties": { "asset": { "type": "string", "description": "Id of the asset." } },
"required": ["asset"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "get_graph",
"title": "Read a graph",
"description": "The graph of a behavior, state machine, shader, particles, sound FX or instrument: each node with its settings and its input and output ports as they are now, the edges between ports (a link of a routing with its send), and the groups and comments (notes). A node that declares its own ports lists them under ports.",
"inputSchema": {
"type": "object",
"properties": { "asset": { "type": "string", "description": "Id of the asset holding the graph." } },
"required": ["asset"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "lint_graph",
"title": "Check how a graph reads",
"description": "Counts what makes a graph hard to read, as the user sees it: nodes on top of each other (overlaps), links that cross (crossings), links running over a node that is not theirs (throughNodes), links running right to left (backwards), comments a node lies on (coveredNotes), and the longest links, with a few ids of each. Aim for 0 overlaps, 0 throughNodes, 0 backwards and 0 coveredNotes, and for no more crossings than the links need: two links leaving one node far apart, like exec and dt, cross what comes in between them. To mend: the command arrange_graph, or a second Get or Owner node next to a far-off user instead of one long link; a covered comment goes back by its nodes with fit_group.",
"inputSchema": {
"type": "object",
"properties": { "asset": { "type": "string", "description": "Id of the asset holding the graph." } },
"required": ["asset"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "catalog",
"title": "What can be made",
"description": "Looks things up before an edit. topic \"commands\": the commands run_commands knows by area, each with what it does; with query (an area, or command names), those commands with their arguments. topic \"nodes\" with asset: the nodes that graph can hold; with query, the best matches in full (ports, settings). topic \"asset_types\": the kinds of asset; with type, that kind's properties. topic \"variable_kinds\": what a variable can hold.",
"inputSchema": {
"type": "object",
"properties": {
"topic": { "type": "string", "enum": ["commands", "nodes", "asset_types", "variable_kinds"] },
"asset": { "type": "string", "description": "For nodes: id of the graph asset." },
"query": {
"type": "string",
"description": "For nodes: words to search for, like \"key held\" or \"move\". For commands: an area or command names, like \"graphs\" or \"add_port add_group\"."
},
"type": { "type": "string", "description": "For asset_types: one type to describe in full." }
},
"required": ["topic"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "run_commands",
"title": "Change the project",
"description": "Runs commands (catalog topic \"commands\") in order as ONE undo step: if one fails, none of them stays, and the answer names the step. A step with \"as\": \"name\" lets later steps use what it made: \"$name\" is its id, \"$name.closing\" another id it names. Each step is built after the one before has run, so a step can add nodes to a behavior made in the same call. Deleting an asset asks the user first. Unless the user switched it off, the editor then shows what the call worked on last: the graph's tab with the nodes it touched selected, the scene (switching to it within the same undo step) or container with its objects selected, or the asset in the inspector.",
"inputSchema": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"command": { "type": "string", "minLength": 1, "description": "Name of the command." },
"args": { "type": "object", "description": "Its arguments, as the catalog lists them." },
"as": { "type": "string", "minLength": 1, "description": "A name for what this step makes." }
},
"required": ["command"],
"additionalProperties": false
}
},
"label": {
"type": "string",
"minLength": 1,
"description": "What the edit does, in a few words: the user sees it as the undo step."
}
},
"required": ["steps"],
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
},
{
"name": "open",
"title": "Show something",
"description": "Brings up an asset's tab (a scene becomes the scene being edited), or the scene or game tab. run_commands shows what it changed by itself; this is for anything else the user should see.",
"inputSchema": {
"type": "object",
"properties": {
"asset": { "type": "string", "description": "Id of the asset to open." },
"tab": { "type": "string", "enum": ["scene", "game"] }
},
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "destructiveHint": false, "openWorldHint": false }
},
{
"name": "select",
"title": "Select",
"description": "Selects an asset, placed objects of a scene or container, or nodes of a graph – what the inspector then shows. kind \"none\" clears the selection.",
"inputSchema": {
"type": "object",
"properties": {
"kind": { "type": "string", "enum": ["none", "asset", "objects", "nodes"] },
"asset": {
"type": "string",
"description": "The asset (kind asset), or the graph asset (kind nodes)."
},
"in": { "type": "string", "description": "Kind objects: the scene or container." },
"objects": { "type": "array", "items": { "type": "string" } },
"nodes": { "type": "array", "items": { "type": "string" } }
},
"required": ["kind"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "destructiveHint": false, "openWorldHint": false }
},
{
"name": "play",
"title": "Play the game",
"description": "Plays the game in the editor, pauses, resumes or stops it. With until, play, resume or pause let the game run on to that game time, as get_runtime gives it, and hold it there, exactly: the call answers once it is paused, and get_runtime and screenshot then show that moment. Edits made while it runs change the running copy only and are gone when it stops.",
"inputSchema": {
"type": "object",
"properties": {
"action": { "type": "string", "enum": ["play", "pause", "resume", "stop"] },
"until": {
"type": "number",
"minimum": 0,
"description": "Game seconds to pause at, at most 30 ahead. A Load scene or Restart game starts the time over and calls it off."
}
},
"required": ["action"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "destructiveHint": false, "openWorldHint": false }
},
{
"name": "play_input",
"title": "Keys and mouse in the running game",
"description": "Plays keys and the mouse into the running game like a player, step by step; each step takes at least one frame, and hold and wait count game seconds. A step does one thing: {\"key\": \"ArrowLeft\", \"hold\": 0.5} holds a key; {\"key\": \"Space\"} taps it for one frame; {\"key\": \"Shift\", \"keep\": \"down\"} presses it and leaves it held after the call, \"keep\": \"up\" lets it go; {\"mouse\": \"click\", \"at\": {\"object\": \"<object id>\"}} or {\"mouse\": \"click\", \"at\": {\"x\": 10, \"y\": -20}} clicks the left button there (world units, as get_runtime gives them); \"down\", \"up\" and \"move\" do just that; {\"wheel\": -1} turns the wheel; {\"wait\": 1} lets the game run on; {\"pause\": true}, the last step, holds the game right after the step before, a wait to its time exactly, so get_runtime and screenshot show that moment. Keys go by their KeyboardEvent.key name: \"a\", \"ArrowUp\", \"Enter\", \"Space\". Answers the game time it took and the game time it is at, whether it is paused, the keys still held, where the mouse is, whether a click on an object landed on it, and the new console lines; read positions and variables with get_runtime. Needs the game playing, not paused, and the editor tab in front.",
"inputSchema": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"key": { "type": "string", "minLength": 1 },
"hold": {
"type": "number",
"minimum": 0,
"maximum": 10,
"description": "Seconds the key is held."
},
"keep": { "type": "string", "enum": ["down", "up"] },
"mouse": { "type": "string", "enum": ["click", "down", "up", "move"] },
"at": {
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" },
"object": { "type": "string", "description": "An object id (or name) of the running game." }
},
"additionalProperties": false
},
"wheel": { "type": "integer", "minimum": -10, "maximum": 10 },
"wait": { "type": "number", "minimum": 0, "maximum": 10, "description": "Game seconds." },
"pause": { "type": "boolean", "description": "true: holds the game here; the last step." }
},
"additionalProperties": false
}
}
},
"required": ["steps"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "destructiveHint": false, "openWorldHint": false }
},
{
"name": "get_runtime",
"title": "Read the running game",
"description": "The game as it runs now: game time, whether it is paused, the camera, the keys held and the mouse, every object with its id, type and position in world units (behaviors with their variables), the values of the Variables assets, the sound (audio), and the last console lines. Under audio: the audio clock (with a hint when it stands still while the game runs: then nothing is heard); the level of the master, of each bus of the mix, of each pattern, song or composition playing and of the tracks of a song, as peak and RMS in dB over the last window seconds (silent below -90 dB; held while the game is paused); the voices sounding now (a sound, or an instrument with its note), each with its bus and the object it belongs to (by); planned, the notes already set to start; the patterns, songs and compositions playing; waiting, the voices whose bytes have not arrived; and failed, the files that would not load. Name objects (ids or names) to get all their properties.",
"inputSchema": {
"type": "object",
"properties": {
"objects": { "type": "array", "items": { "type": "string" } },
"console": {
"type": "integer",
"minimum": 0,
"maximum": 500,
"description": "How many of the last console lines to give (default 30)."
}
},
"additionalProperties": false
},
"annotations": { "readOnlyHint": true, "openWorldHint": false }
},
{
"name": "undo",
"title": "Undo",
"description": "Takes back the last edits, the user's as well as yours, like Ctrl+Z; one run_commands call is one step.",
"inputSchema": {
"type": "object",
"properties": { "steps": { "type": "integer", "minimum": 1, "maximum": 20 } },
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "destructiveHint": false, "openWorldHint": false }
},
{
"name": "redo",
"title": "Redo",
"description": "Does the edits again that undo took back, like Ctrl+Y.",
"inputSchema": {
"type": "object",
"properties": { "steps": { "type": "integer", "minimum": 1, "maximum": 20 } },
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "destructiveHint": false, "openWorldHint": false }
}
]