Files
feuerwerk/src/editor/agent/tools.json
T
adminandClaude Opus 5.5 b9d1602b94 Arrange graph nodes for the agent and with a button (#429)
An agent that built a graph through MCP left every new node in one
row. In the first real test that put nine nodes on a line 2080 px
long, with the Owner's links running straight through all the others.
Placing nodes by hand cost nine move_node calls and guessed heights.
The agent now gives no coordinates, and the editor lays the nodes out.

- arrange.ts is a pure function. It puts the flow into columns from
  left to right and sets an Owner or Get right before the first node
  it feeds. It orders each column for the fewest crossings and lines
  nodes up by their linked ports without overlaps.
- Node sizes come from the drawn NodeView in the browser and from an
  estimate in unit tests. Both share the constants in nodeBox.ts.
- run_commands arranges the nodes that came without x and y when the
  call ends, inside the same undo step. Nodes that were there stay.
- New command arrange_graph and new tool lint_graph. The lint counts
  overlaps, crossings, links through foreign nodes, backward links
  and the longest links, with example ids.
- The relay instructions tell the agent not to give coordinates and
  to fetch far values with a second Get or Owner node.
- An Arrange button next to the snap magnet in all eight graph tabs.
  It arranges the selected nodes, or all of them, as one undo step.

Found while testing on a real project:

- lint_graph missed a crossing where two links swap rows exactly
  mirrored. Both curves then share a sample point in the middle, and
  only a crossing between two sample points was counted. Links that
  meet on a point count now (segmentsMeet).
- The Arrow-key graph with one Owner for four Moves cannot get below
  8 crossings. The Owner feeds object in row 2 of Move, between exec
  and dt of the key, so it always crosses one of them.

Tests:
- graph-arrange, agent-edit, snap-toggle: 33/33. The swapped-rows
  test was red before the fix (0 instead of 1).
- Four counter-checks that bend arrange or the edit plan, all red.
- e2e 71 on melektaus-atx: 18/18. Lint after the build shows 0
  overlaps, 0 links through nodes, 0 backward. Arrange takes a
  tangled graph from 3 backward links and 2 crossings to 0, and one
  Ctrl+Z restores it.
- Gate on melektaus-atx: CHECK EXIT 0, 318 files, 4917 tests,
  measured before the lint fix.
- Live through MCP on the user's project: lint_graph reports 8
  crossings after Arrange, matching the screenshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:09:52 +02:00

207 lines
9.1 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 assets with their ids, names and types, and how many objects or nodes each holds. Filter 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": "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. 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, and the groups and notes.",
"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), and the longest links, with a few ids of each. Aim for 0 overlaps and 0 throughNodes. To mend: the command arrange_graph, or a second Get or Owner node next to a far-off user instead of one long link.",
"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\": every command run_commands knows, with its 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\"."
},
"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.",
"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.",
"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. 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"] } },
"required": ["action"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "destructiveHint": false, "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 }
}
]