@skillcap/gdh 3.2.3 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/README.md +6 -5
  3. package/RELEASE-SPAN-UPDATE-CONTRACTS.json +125 -0
  4. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  5. package/node_modules/@gdh/adapters/dist/index.js +0 -2
  6. package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
  7. package/node_modules/@gdh/adapters/dist/skill-rendering.d.ts.map +1 -1
  8. package/node_modules/@gdh/adapters/dist/skill-rendering.js +26 -30
  9. package/node_modules/@gdh/adapters/dist/skill-rendering.js.map +1 -1
  10. package/node_modules/@gdh/adapters/package.json +7 -7
  11. package/node_modules/@gdh/authoring/dist/lsp.d.ts +12 -0
  12. package/node_modules/@gdh/authoring/dist/lsp.d.ts.map +1 -1
  13. package/node_modules/@gdh/authoring/dist/lsp.js +128 -3
  14. package/node_modules/@gdh/authoring/dist/lsp.js.map +1 -1
  15. package/node_modules/@gdh/authoring/package.json +2 -2
  16. package/node_modules/@gdh/cli/dist/bridge-session.d.ts.map +1 -1
  17. package/node_modules/@gdh/cli/dist/bridge-session.js +11 -25
  18. package/node_modules/@gdh/cli/dist/bridge-session.js.map +1 -1
  19. package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
  20. package/node_modules/@gdh/cli/dist/index.js +26 -537
  21. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  22. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  23. package/node_modules/@gdh/cli/dist/migrate.js +25 -0
  24. package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
  25. package/node_modules/@gdh/cli/package.json +11 -11
  26. package/node_modules/@gdh/core/dist/editor-operation-runner.d.ts.map +1 -1
  27. package/node_modules/@gdh/core/dist/editor-operation-runner.js +530 -1
  28. package/node_modules/@gdh/core/dist/editor-operation-runner.js.map +1 -1
  29. package/node_modules/@gdh/core/dist/index.d.ts +14 -10
  30. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  31. package/node_modules/@gdh/core/dist/index.js +16 -14
  32. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  33. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.d.ts +3 -0
  34. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.d.ts.map +1 -0
  35. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.js +91 -0
  36. package/node_modules/@gdh/core/dist/migrations/entries/s3c26_to_s3c27_run_game_knowledge.js.map +1 -0
  37. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts +7 -2
  38. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts.map +1 -1
  39. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js +9 -3
  40. package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js.map +1 -1
  41. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.d.ts +20 -9
  42. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.d.ts.map +1 -1
  43. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js +22 -10
  44. package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js.map +1 -1
  45. package/node_modules/@gdh/core/dist/migrations/registry.d.ts +1 -1
  46. package/node_modules/@gdh/core/dist/migrations/registry.d.ts.map +1 -1
  47. package/node_modules/@gdh/core/dist/migrations/registry.js +2 -0
  48. package/node_modules/@gdh/core/dist/migrations/registry.js.map +1 -1
  49. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.d.ts +16 -0
  50. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.d.ts.map +1 -0
  51. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.js +89 -0
  52. package/node_modules/@gdh/core/dist/run-game-knowledge-scaffold.js.map +1 -0
  53. package/node_modules/@gdh/core/package.json +1 -1
  54. package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
  55. package/node_modules/@gdh/docs/dist/guidance.js +29 -0
  56. package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
  57. package/node_modules/@gdh/docs/dist/templates/guidance/editor-bridge.md.tpl +4 -1
  58. package/node_modules/@gdh/docs/dist/templates/guidance/runtime.md.tpl +33 -0
  59. package/node_modules/@gdh/docs/package.json +2 -2
  60. package/node_modules/@gdh/editor/dist/index.d.ts +101 -0
  61. package/node_modules/@gdh/editor/dist/index.d.ts.map +1 -1
  62. package/node_modules/@gdh/editor/dist/index.js +166 -5
  63. package/node_modules/@gdh/editor/dist/index.js.map +1 -1
  64. package/node_modules/@gdh/editor/package.json +2 -2
  65. package/node_modules/@gdh/mcp/dist/bridge-tools.d.ts +3 -4
  66. package/node_modules/@gdh/mcp/dist/bridge-tools.d.ts.map +1 -1
  67. package/node_modules/@gdh/mcp/dist/bridge-tools.js +5 -77
  68. package/node_modules/@gdh/mcp/dist/bridge-tools.js.map +1 -1
  69. package/node_modules/@gdh/mcp/dist/index.d.ts.map +1 -1
  70. package/node_modules/@gdh/mcp/dist/index.js +832 -249
  71. package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
  72. package/node_modules/@gdh/mcp/package.json +8 -8
  73. package/node_modules/@gdh/observability/package.json +2 -2
  74. package/node_modules/@gdh/runtime/dist/bridge-broker.d.ts +9 -1
  75. package/node_modules/@gdh/runtime/dist/bridge-broker.d.ts.map +1 -1
  76. package/node_modules/@gdh/runtime/dist/bridge-broker.js +267 -52
  77. package/node_modules/@gdh/runtime/dist/bridge-broker.js.map +1 -1
  78. package/node_modules/@gdh/runtime/dist/bridge-surface.js +11 -3
  79. package/node_modules/@gdh/runtime/dist/bridge-surface.js.map +1 -1
  80. package/node_modules/@gdh/runtime/package.json +2 -2
  81. package/node_modules/@gdh/scan/dist/onboard.d.ts.map +1 -1
  82. package/node_modules/@gdh/scan/dist/onboard.js +60 -180
  83. package/node_modules/@gdh/scan/dist/onboard.js.map +1 -1
  84. package/node_modules/@gdh/scan/package.json +3 -3
  85. package/node_modules/@gdh/verify/package.json +6 -6
  86. package/package.json +12 -12
@@ -1,3 +1,5 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
1
3
  import { doctorAuthoringDiagnostics, getAuthoringDiagnosticsStatus, getCurrentAuthoringDiagnostics, readProjectConfig, readWorktreeState, refreshAuthoringDiagnostics, resolveAuthoringStatus, resolveEffectiveTargetPath, resolveTargetGodotDocsVersion, runAuthoringCheck, runTargetPrepare, warmupManagedLsp, } from "@gdh/authoring";
2
4
  import { definePackageBoundary, GDH_MCP_MANIFEST_VERSION, presentPublicRuntimeTerms, readGdhUpdateMetaOrNull, resolveGdhProductMetadata, } from "@gdh/core";
3
5
  import { fetchOfficialGodotDoc, searchOfficialGodotDocs } from "@gdh/docs";
@@ -185,84 +187,599 @@ const TOOL_MANIFEST = [
185
187
  outputContract: "GdhAuthoringLspWarmupResult",
186
188
  },
187
189
  {
188
- name: "run-config.check",
189
- summary: "Evaluate whether one run configuration is runnable, blocked, or misconfigured for the bound target.",
190
+ name: "editor.session.status",
191
+ summary: "Inspect whether GDH can run Godot-native editor operations for the exact bound target/worktree.",
192
+ targetPathPolicy: "optional",
193
+ inputSchema: defineObjectSchema("Editor Bridge session status request.", {
194
+ mode: {
195
+ type: "string",
196
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled. Defaults to auto.",
197
+ },
198
+ godotEditorBin: {
199
+ type: "string",
200
+ description: "Optional Godot editor executable override for this invocation.",
201
+ },
202
+ }),
203
+ outputSchema: outputEnvelopeSchema,
204
+ outputContract: "GdhEditorSessionSummary",
205
+ },
206
+ {
207
+ name: "editor.state",
208
+ summary: "Read current Godot editor context for the exact target/worktree: project state plus current/open scene and selection when an adopted editor exposes them.",
209
+ targetPathPolicy: "optional",
210
+ inputSchema: defineObjectSchema("Editor state request.", {
211
+ mode: {
212
+ type: "string",
213
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled. Use adopt_only when the user asks about the already-open editor.",
214
+ },
215
+ timeoutMs: {
216
+ type: "number",
217
+ description: "Optional positive timeout in milliseconds for the editor operation.",
218
+ },
219
+ godotEditorBin: {
220
+ type: "string",
221
+ description: "Optional Godot editor executable override for this invocation.",
222
+ },
223
+ }),
224
+ outputSchema: outputEnvelopeSchema,
225
+ outputContract: "GdhEditorOperationResult",
226
+ },
227
+ {
228
+ name: "editor.scene.tree",
229
+ summary: "Load one saved scene through Godot editor APIs and return a bounded node tree. Requires scenePath; use editor.state first when you need the active editor scene.",
230
+ targetPathPolicy: "optional",
231
+ inputSchema: defineObjectSchema("Editor scene tree request.", {
232
+ scenePath: {
233
+ type: "string",
234
+ description: "Required res:// path for the saved scene to load.",
235
+ },
236
+ maxDepth: {
237
+ type: "number",
238
+ description: "Optional positive depth limit for returned children.",
239
+ },
240
+ mode: {
241
+ type: "string",
242
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
243
+ },
244
+ timeoutMs: {
245
+ type: "number",
246
+ description: "Optional positive timeout in milliseconds for the editor operation.",
247
+ },
248
+ godotEditorBin: {
249
+ type: "string",
250
+ description: "Optional Godot editor executable override for this invocation.",
251
+ },
252
+ }, ["scenePath"]),
253
+ outputSchema: outputEnvelopeSchema,
254
+ outputContract: "GdhEditorOperationResult",
255
+ },
256
+ {
257
+ name: "editor.node.inspect",
258
+ summary: "Inspect one node from a saved scene: node summary plus requested property values or a bounded inspector-style property list.",
259
+ targetPathPolicy: "optional",
260
+ inputSchema: defineObjectSchema("Editor node inspection request.", {
261
+ scenePath: {
262
+ type: "string",
263
+ description: "Required res:// path for the saved scene to load.",
264
+ },
265
+ nodePath: {
266
+ type: "string",
267
+ description: "Optional node path relative to the scene root. Defaults to root.",
268
+ },
269
+ propertyNames: stringArraySchema("Optional exact property names to read from the node."),
270
+ includePropertyList: {
271
+ type: "boolean",
272
+ description: "When true, return bounded property metadata similar to inspector discovery.",
273
+ },
274
+ propertyQuery: {
275
+ type: "string",
276
+ description: "Optional substring filter for returned property metadata.",
277
+ },
278
+ propertyLimit: {
279
+ type: "number",
280
+ description: "Optional positive limit for returned property metadata.",
281
+ },
282
+ mode: {
283
+ type: "string",
284
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
285
+ },
286
+ timeoutMs: {
287
+ type: "number",
288
+ description: "Optional positive timeout in milliseconds for the editor operation.",
289
+ },
290
+ godotEditorBin: {
291
+ type: "string",
292
+ description: "Optional Godot editor executable override for this invocation.",
293
+ },
294
+ }, ["scenePath"]),
295
+ outputSchema: outputEnvelopeSchema,
296
+ outputContract: "GdhEditorOperationResult",
297
+ },
298
+ {
299
+ name: "editor.resource.read",
300
+ summary: "Read requested properties from one Godot resource via editor APIs. Nested arrays/dictionaries are encoded recursively with bounded resource handles.",
301
+ targetPathPolicy: "optional",
302
+ inputSchema: defineObjectSchema("Editor resource read request.", {
303
+ resourcePath: {
304
+ type: "string",
305
+ description: "Required res:// resource path, including ::subresource id when needed.",
306
+ },
307
+ propertyNames: stringArraySchema("Optional exact property names to read from the resource."),
308
+ mode: {
309
+ type: "string",
310
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
311
+ },
312
+ timeoutMs: {
313
+ type: "number",
314
+ description: "Optional positive timeout in milliseconds for the editor operation.",
315
+ },
316
+ godotEditorBin: {
317
+ type: "string",
318
+ description: "Optional Godot editor executable override for this invocation.",
319
+ },
320
+ }, ["resourcePath"]),
321
+ outputSchema: outputEnvelopeSchema,
322
+ outputContract: "GdhEditorOperationResult",
323
+ },
324
+ {
325
+ name: "editor.resource.properties",
326
+ summary: "Discover bounded property metadata for one Godot resource before choosing propertyNames for editor.resource.read.",
327
+ targetPathPolicy: "optional",
328
+ inputSchema: defineObjectSchema("Editor resource property discovery request.", {
329
+ resourcePath: {
330
+ type: "string",
331
+ description: "Required res:// resource path, including ::subresource id when needed.",
332
+ },
333
+ propertyQuery: {
334
+ type: "string",
335
+ description: "Optional substring filter for returned property metadata.",
336
+ },
337
+ propertyLimit: {
338
+ type: "number",
339
+ description: "Optional positive limit for returned property metadata.",
340
+ },
341
+ mode: {
342
+ type: "string",
343
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
344
+ },
345
+ timeoutMs: {
346
+ type: "number",
347
+ description: "Optional positive timeout in milliseconds for the editor operation.",
348
+ },
349
+ godotEditorBin: {
350
+ type: "string",
351
+ description: "Optional Godot editor executable override for this invocation.",
352
+ },
353
+ }, ["resourcePath"]),
354
+ outputSchema: outputEnvelopeSchema,
355
+ outputContract: "GdhEditorOperationResult",
356
+ },
357
+ {
358
+ name: "editor.class.search",
359
+ summary: "Search Godot ClassDB and registered script classes with bounded query controls.",
190
360
  targetPathPolicy: "optional",
191
- inputSchema: defineObjectSchema("Run-configuration check request.", {
192
- runConfigurationId: {
361
+ inputSchema: defineObjectSchema("Editor class search request.", {
362
+ query: {
363
+ type: "string",
364
+ description: "Optional substring query for class names.",
365
+ },
366
+ baseClass: {
367
+ type: "string",
368
+ description: "Optional base class filter.",
369
+ },
370
+ instantiableOnly: {
371
+ type: "boolean",
372
+ description: "When false, include non-instantiable classes. Defaults to true.",
373
+ },
374
+ limit: {
375
+ type: "number",
376
+ description: "Optional positive result limit.",
377
+ },
378
+ mode: {
379
+ type: "string",
380
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
381
+ },
382
+ timeoutMs: {
383
+ type: "number",
384
+ description: "Optional positive timeout in milliseconds for the editor operation.",
385
+ },
386
+ godotEditorBin: {
387
+ type: "string",
388
+ description: "Optional Godot editor executable override for this invocation.",
389
+ },
390
+ }),
391
+ outputSchema: outputEnvelopeSchema,
392
+ outputContract: "GdhEditorOperationResult",
393
+ },
394
+ {
395
+ name: "editor.class.info",
396
+ summary: "Return bounded property metadata for one Godot or registered script class.",
397
+ targetPathPolicy: "optional",
398
+ inputSchema: defineObjectSchema("Editor class info request.", {
399
+ className: {
400
+ type: "string",
401
+ description: "Required Godot or registered script class name.",
402
+ },
403
+ includeInherited: {
404
+ type: "boolean",
405
+ description: "When true, include inherited properties. Defaults to true.",
406
+ },
407
+ propertyQuery: {
408
+ type: "string",
409
+ description: "Optional substring filter for returned property metadata.",
410
+ },
411
+ propertyLimit: {
412
+ type: "number",
413
+ description: "Optional positive property metadata limit.",
414
+ },
415
+ mode: {
416
+ type: "string",
417
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
418
+ },
419
+ timeoutMs: {
420
+ type: "number",
421
+ description: "Optional positive timeout in milliseconds for the editor operation.",
422
+ },
423
+ godotEditorBin: {
424
+ type: "string",
425
+ description: "Optional Godot editor executable override for this invocation.",
426
+ },
427
+ }, ["className"]),
428
+ outputSchema: outputEnvelopeSchema,
429
+ outputContract: "GdhEditorOperationResult",
430
+ },
431
+ {
432
+ name: "editor.animation.player.inspect",
433
+ summary: "Inspect an AnimationPlayer or AnimationMixer node in a saved scene, including libraries, clips, autoplay, and blend metadata.",
434
+ targetPathPolicy: "optional",
435
+ inputSchema: defineObjectSchema("Editor animation player inspection request.", {
436
+ scenePath: {
437
+ type: "string",
438
+ description: "Required res:// path for the saved scene to load.",
439
+ },
440
+ animationPlayerPath: {
441
+ type: "string",
442
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
443
+ },
444
+ mode: {
445
+ type: "string",
446
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
447
+ },
448
+ timeoutMs: {
449
+ type: "number",
450
+ description: "Optional positive timeout in milliseconds for the editor operation.",
451
+ },
452
+ godotEditorBin: {
453
+ type: "string",
454
+ description: "Optional Godot editor executable override for this invocation.",
455
+ },
456
+ }, ["scenePath", "animationPlayerPath"]),
457
+ outputSchema: outputEnvelopeSchema,
458
+ outputContract: "GdhEditorOperationResult",
459
+ },
460
+ {
461
+ name: "editor.animation.clip.read",
462
+ summary: "Read one AnimationPlayer clip through Godot APIs, including tracks and optionally bounded keyframe data.",
463
+ targetPathPolicy: "optional",
464
+ inputSchema: defineObjectSchema("Editor animation clip read request.", {
465
+ scenePath: {
466
+ type: "string",
467
+ description: "Required res:// path for the saved scene to load.",
468
+ },
469
+ animationPlayerPath: {
470
+ type: "string",
471
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
472
+ },
473
+ libraryName: {
474
+ type: "string",
475
+ description: "Optional AnimationLibrary name. Defaults to the global empty-name library.",
476
+ },
477
+ animationName: {
478
+ type: "string",
479
+ description: "Required animation key inside the selected library.",
480
+ },
481
+ includeKeys: {
482
+ type: "boolean",
483
+ description: "When true, include bounded keyframe data for each returned track.",
484
+ },
485
+ keyLimit: {
486
+ type: "number",
487
+ description: "Optional positive keyframe limit across each track.",
488
+ },
489
+ mode: {
490
+ type: "string",
491
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
492
+ },
493
+ timeoutMs: {
494
+ type: "number",
495
+ description: "Optional positive timeout in milliseconds for the editor operation.",
496
+ },
497
+ godotEditorBin: {
498
+ type: "string",
499
+ description: "Optional Godot editor executable override for this invocation.",
500
+ },
501
+ }, ["scenePath", "animationPlayerPath", "animationName"]),
502
+ outputSchema: outputEnvelopeSchema,
503
+ outputContract: "GdhEditorOperationResult",
504
+ },
505
+ {
506
+ name: "editor.animation.clip.create",
507
+ summary: "Create or overwrite one AnimationPlayer clip in an AnimationLibrary through Godot editor APIs.",
508
+ targetPathPolicy: "optional",
509
+ inputSchema: defineObjectSchema("Editor animation clip create request.", {
510
+ scenePath: {
511
+ type: "string",
512
+ description: "Required res:// path for the saved scene to load.",
513
+ },
514
+ animationPlayerPath: {
515
+ type: "string",
516
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
517
+ },
518
+ libraryName: {
519
+ type: "string",
520
+ description: "Optional AnimationLibrary name. Defaults to the global empty-name library.",
521
+ },
522
+ animationName: {
523
+ type: "string",
524
+ description: "Required animation key inside the selected library.",
525
+ },
526
+ length: {
527
+ type: "number",
528
+ description: "Optional animation length in seconds. Defaults to 1.0.",
529
+ },
530
+ step: {
531
+ type: "number",
532
+ description: "Optional animation step in seconds.",
533
+ },
534
+ loopMode: {
535
+ type: ["string", "number"],
536
+ description: "Optional loop mode: none, linear, pingpong, or a Godot numeric value.",
537
+ },
538
+ overwrite: {
539
+ type: "boolean",
540
+ description: "When true, replace an existing clip with the same name.",
541
+ },
542
+ mode: {
543
+ type: "string",
544
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
545
+ },
546
+ timeoutMs: {
547
+ type: "number",
548
+ description: "Optional positive timeout in milliseconds for the editor operation.",
549
+ },
550
+ godotEditorBin: {
551
+ type: "string",
552
+ description: "Optional Godot editor executable override for this invocation.",
553
+ },
554
+ }, ["scenePath", "animationPlayerPath", "animationName"]),
555
+ outputSchema: outputEnvelopeSchema,
556
+ outputContract: "GdhEditorOperationResult",
557
+ },
558
+ {
559
+ name: "editor.animation.clip.delete",
560
+ summary: "Delete one AnimationPlayer clip from an AnimationLibrary through Godot editor APIs.",
561
+ targetPathPolicy: "optional",
562
+ inputSchema: defineObjectSchema("Editor animation clip delete request.", {
563
+ scenePath: {
564
+ type: "string",
565
+ description: "Required res:// path for the saved scene to load.",
566
+ },
567
+ animationPlayerPath: {
568
+ type: "string",
569
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
570
+ },
571
+ libraryName: {
572
+ type: "string",
573
+ description: "Optional AnimationLibrary name. Defaults to the global empty-name library.",
574
+ },
575
+ animationName: {
576
+ type: "string",
577
+ description: "Required animation key inside the selected library.",
578
+ },
579
+ mode: {
580
+ type: "string",
581
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
582
+ },
583
+ timeoutMs: {
584
+ type: "number",
585
+ description: "Optional positive timeout in milliseconds for the editor operation.",
586
+ },
587
+ godotEditorBin: {
588
+ type: "string",
589
+ description: "Optional Godot editor executable override for this invocation.",
590
+ },
591
+ }, ["scenePath", "animationPlayerPath", "animationName"]),
592
+ outputSchema: outputEnvelopeSchema,
593
+ outputContract: "GdhEditorOperationResult",
594
+ },
595
+ {
596
+ name: "editor.animation.track.add",
597
+ summary: "Add a typed Animation track to one clip, using Godot track types and a NodePath such as Sprite2D:modulate:a.",
598
+ targetPathPolicy: "optional",
599
+ inputSchema: defineObjectSchema("Editor animation track add request.", {
600
+ scenePath: {
601
+ type: "string",
602
+ description: "Required res:// path for the saved scene to load.",
603
+ },
604
+ animationPlayerPath: {
605
+ type: "string",
606
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
607
+ },
608
+ libraryName: {
609
+ type: "string",
610
+ description: "Optional AnimationLibrary name. Defaults to the global empty-name library.",
611
+ },
612
+ animationName: {
613
+ type: "string",
614
+ description: "Required animation key inside the selected library.",
615
+ },
616
+ trackType: {
617
+ type: ["string", "number"],
618
+ description: "Optional Godot Animation track type. Use value, position_3d, rotation_3d, scale_3d, blend_shape, method, bezier, audio, or animation. Defaults to value.",
619
+ },
620
+ trackPath: {
621
+ type: "string",
622
+ description: "Required Animation track NodePath, relative to the AnimationPlayer root.",
623
+ },
624
+ atPosition: {
625
+ type: "number",
626
+ description: "Optional zero-based insertion position. Defaults to appending the track.",
627
+ },
628
+ interpolation: {
629
+ type: ["string", "number"],
630
+ description: "Optional interpolation: nearest, linear, cubic, linear_angle, cubic_angle, or a Godot numeric value.",
631
+ },
632
+ updateMode: {
633
+ type: ["string", "number"],
634
+ description: "Optional value-track update mode: continuous, discrete, capture, or a Godot numeric value.",
635
+ },
636
+ mode: {
637
+ type: "string",
638
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
639
+ },
640
+ timeoutMs: {
641
+ type: "number",
642
+ description: "Optional positive timeout in milliseconds for the editor operation.",
643
+ },
644
+ godotEditorBin: {
645
+ type: "string",
646
+ description: "Optional Godot editor executable override for this invocation.",
647
+ },
648
+ }, ["scenePath", "animationPlayerPath", "animationName", "trackPath"]),
649
+ outputSchema: outputEnvelopeSchema,
650
+ outputContract: "GdhEditorOperationResult",
651
+ },
652
+ {
653
+ name: "editor.animation.track.remove",
654
+ summary: "Remove one track from an AnimationPlayer clip by zero-based track index.",
655
+ targetPathPolicy: "optional",
656
+ inputSchema: defineObjectSchema("Editor animation track remove request.", {
657
+ scenePath: {
658
+ type: "string",
659
+ description: "Required res:// path for the saved scene to load.",
660
+ },
661
+ animationPlayerPath: {
662
+ type: "string",
663
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
664
+ },
665
+ libraryName: {
666
+ type: "string",
667
+ description: "Optional AnimationLibrary name. Defaults to the global empty-name library.",
668
+ },
669
+ animationName: {
193
670
  type: "string",
194
- description: "Run configuration id to evaluate.",
671
+ description: "Required animation key inside the selected library.",
195
672
  },
196
- provider: {
673
+ trackIndex: {
674
+ type: "number",
675
+ description: "Required zero-based track index.",
676
+ },
677
+ mode: {
197
678
  type: "string",
198
- description: "Optional provider id to force when a run configuration supports multiple providers.",
679
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
199
680
  },
200
- parameters: stringMapSchema("Optional parameter overrides keyed by parameter id."),
201
- enabledFeatures: stringArraySchema("Feature flag ids to force on for this check."),
202
- disabledFeatures: stringArraySchema("Feature flag ids to force off for this check."),
203
- environment: stringMapSchema("Environment variable overrides keyed by variable name."),
204
- liveWorkspace: {
205
- type: "boolean",
206
- description: "When true, evaluate the run configuration against the live workspace instead of an isolated copy.",
681
+ timeoutMs: {
682
+ type: "number",
683
+ description: "Optional positive timeout in milliseconds for the editor operation.",
207
684
  },
208
- }, ["runConfigurationId"]),
685
+ godotEditorBin: {
686
+ type: "string",
687
+ description: "Optional Godot editor executable override for this invocation.",
688
+ },
689
+ }, ["scenePath", "animationPlayerPath", "animationName", "trackIndex"]),
209
690
  outputSchema: outputEnvelopeSchema,
210
- outputContract: "GdhRuntimeRecipeCheckResult",
691
+ outputContract: "GdhEditorOperationResult",
211
692
  },
212
693
  {
213
- name: "run-config.run",
214
- summary: "Run one bound-target run configuration and return the structured run result.",
694
+ name: "editor.animation.keyframe.upsert",
695
+ summary: "Insert or replace one keyframe on an Animation track. Values use GDH's encoded Variant shape for vectors, colors, resources, and scalars.",
215
696
  targetPathPolicy: "optional",
216
- inputSchema: defineObjectSchema("Run-configuration execution request.", {
217
- runConfigurationId: {
697
+ inputSchema: defineObjectSchema("Editor animation keyframe upsert request.", {
698
+ scenePath: {
218
699
  type: "string",
219
- description: "Run configuration id to execute.",
700
+ description: "Required res:// path for the saved scene to load.",
220
701
  },
221
- provider: {
702
+ animationPlayerPath: {
222
703
  type: "string",
223
- description: "Optional provider id to force when a run configuration supports multiple providers.",
704
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
224
705
  },
225
- parameters: stringMapSchema("Optional parameter overrides keyed by parameter id."),
226
- enabledFeatures: stringArraySchema("Feature flag ids to force on for this run."),
227
- disabledFeatures: stringArraySchema("Feature flag ids to force off for this run."),
228
- environment: stringMapSchema("Environment variable overrides keyed by variable name."),
229
- liveWorkspace: {
230
- type: "boolean",
231
- description: "When true, execute against the live workspace instead of an isolated copy.",
706
+ libraryName: {
707
+ type: "string",
708
+ description: "Optional AnimationLibrary name. Defaults to the global empty-name library.",
232
709
  },
233
- renderedScreenshot: {
234
- type: "boolean",
235
- description: "When true, request one rendered screenshot artifact for this run when the selected runtime path can capture it.",
710
+ animationName: {
711
+ type: "string",
712
+ description: "Required animation key inside the selected library.",
713
+ },
714
+ trackIndex: {
715
+ type: "number",
716
+ description: "Required zero-based track index.",
717
+ },
718
+ time: {
719
+ type: "number",
720
+ description: "Required key time in seconds.",
721
+ },
722
+ value: {
723
+ type: ["string", "number", "boolean", "object", "array"],
724
+ description: "Required key value as a JSON-serializable Godot Variant encoding.",
725
+ additionalProperties: true,
726
+ },
727
+ transition: {
728
+ type: "number",
729
+ description: "Optional key transition value. Defaults to 1.0.",
236
730
  },
237
- }, ["runConfigurationId"]),
238
- outputSchema: outputEnvelopeSchema,
239
- outputContract: "GdhRuntimeRecipeRunResult",
240
- },
241
- {
242
- name: "editor.session.status",
243
- summary: "Inspect whether GDH can run Godot-native editor operations for the exact bound target/worktree.",
244
- targetPathPolicy: "optional",
245
- inputSchema: defineObjectSchema("Editor Bridge session status request.", {
246
731
  mode: {
247
732
  type: "string",
248
- description: "Optional session mode: auto, headless_only, adopt_only, or disabled. Defaults to auto.",
733
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
734
+ },
735
+ timeoutMs: {
736
+ type: "number",
737
+ description: "Optional positive timeout in milliseconds for the editor operation.",
249
738
  },
250
739
  godotEditorBin: {
251
740
  type: "string",
252
741
  description: "Optional Godot editor executable override for this invocation.",
253
742
  },
254
- }),
743
+ }, ["scenePath", "animationPlayerPath", "animationName", "trackIndex", "time", "value"]),
255
744
  outputSchema: outputEnvelopeSchema,
256
- outputContract: "GdhEditorSessionSummary",
745
+ outputContract: "GdhEditorOperationResult",
257
746
  },
258
747
  {
259
- name: "editor.state",
260
- summary: "Read current Godot editor context for the exact target/worktree: project state plus current/open scene and selection when an adopted editor exposes them.",
748
+ name: "editor.animation.keyframe.delete",
749
+ summary: "Delete one keyframe from an Animation track by key index or key time.",
261
750
  targetPathPolicy: "optional",
262
- inputSchema: defineObjectSchema("Editor state request.", {
751
+ inputSchema: defineObjectSchema("Editor animation keyframe delete request.", {
752
+ scenePath: {
753
+ type: "string",
754
+ description: "Required res:// path for the saved scene to load.",
755
+ },
756
+ animationPlayerPath: {
757
+ type: "string",
758
+ description: "Required node path to the AnimationPlayer or AnimationMixer.",
759
+ },
760
+ libraryName: {
761
+ type: "string",
762
+ description: "Optional AnimationLibrary name. Defaults to the global empty-name library.",
763
+ },
764
+ animationName: {
765
+ type: "string",
766
+ description: "Required animation key inside the selected library.",
767
+ },
768
+ trackIndex: {
769
+ type: "number",
770
+ description: "Required zero-based track index.",
771
+ },
772
+ keyIndex: {
773
+ type: "number",
774
+ description: "Optional zero-based key index. Provide keyIndex or time.",
775
+ },
776
+ time: {
777
+ type: "number",
778
+ description: "Optional key time in seconds. Provide keyIndex or time.",
779
+ },
263
780
  mode: {
264
781
  type: "string",
265
- description: "Optional session mode: auto, headless_only, adopt_only, or disabled. Use adopt_only when the user asks about the already-open editor.",
782
+ description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
266
783
  },
267
784
  timeoutMs: {
268
785
  type: "number",
@@ -272,22 +789,31 @@ const TOOL_MANIFEST = [
272
789
  type: "string",
273
790
  description: "Optional Godot editor executable override for this invocation.",
274
791
  },
275
- }),
792
+ }, ["scenePath", "animationPlayerPath", "animationName", "trackIndex"]),
276
793
  outputSchema: outputEnvelopeSchema,
277
794
  outputContract: "GdhEditorOperationResult",
278
795
  },
279
796
  {
280
- name: "editor.scene.tree",
281
- summary: "Load one saved scene through Godot editor APIs and return a bounded node tree. Requires scenePath; use editor.state first when you need the active editor scene.",
797
+ name: "editor.animation.state_machine.create",
798
+ summary: "Create an AnimationNodeStateMachine tree_root on an AnimationTree node through Godot editor APIs.",
282
799
  targetPathPolicy: "optional",
283
- inputSchema: defineObjectSchema("Editor scene tree request.", {
800
+ inputSchema: defineObjectSchema("Editor animation state-machine create request.", {
284
801
  scenePath: {
285
802
  type: "string",
286
803
  description: "Required res:// path for the saved scene to load.",
287
804
  },
288
- maxDepth: {
289
- type: "number",
290
- description: "Optional positive depth limit for returned children.",
805
+ animationTreePath: {
806
+ type: "string",
807
+ description: "Required node path to the AnimationTree.",
808
+ },
809
+ overwrite: {
810
+ type: "boolean",
811
+ description: "When true, replace an existing AnimationNodeStateMachine tree_root.",
812
+ },
813
+ properties: {
814
+ type: "object",
815
+ description: "Optional properties to set on the AnimationNodeStateMachine resource.",
816
+ additionalProperties: true,
291
817
  },
292
818
  mode: {
293
819
  type: "string",
@@ -301,35 +827,22 @@ const TOOL_MANIFEST = [
301
827
  type: "string",
302
828
  description: "Optional Godot editor executable override for this invocation.",
303
829
  },
304
- }, ["scenePath"]),
830
+ }, ["scenePath", "animationTreePath"]),
305
831
  outputSchema: outputEnvelopeSchema,
306
832
  outputContract: "GdhEditorOperationResult",
307
833
  },
308
834
  {
309
- name: "editor.node.inspect",
310
- summary: "Inspect one node from a saved scene: node summary plus requested property values or a bounded inspector-style property list.",
835
+ name: "editor.animation.state_machine.read",
836
+ summary: "Read an AnimationTree state-machine graph as compact nodes and transitions without automating the editor UI.",
311
837
  targetPathPolicy: "optional",
312
- inputSchema: defineObjectSchema("Editor node inspection request.", {
838
+ inputSchema: defineObjectSchema("Editor animation state-machine graph read request.", {
313
839
  scenePath: {
314
840
  type: "string",
315
841
  description: "Required res:// path for the saved scene to load.",
316
842
  },
317
- nodePath: {
318
- type: "string",
319
- description: "Optional node path relative to the scene root. Defaults to root.",
320
- },
321
- propertyNames: stringArraySchema("Optional exact property names to read from the node."),
322
- includePropertyList: {
323
- type: "boolean",
324
- description: "When true, return bounded property metadata similar to inspector discovery.",
325
- },
326
- propertyQuery: {
843
+ animationTreePath: {
327
844
  type: "string",
328
- description: "Optional substring filter for returned property metadata.",
329
- },
330
- propertyLimit: {
331
- type: "number",
332
- description: "Optional positive limit for returned property metadata.",
845
+ description: "Required node path to the AnimationTree with an AnimationNodeStateMachine tree_root.",
333
846
  },
334
847
  mode: {
335
848
  type: "string",
@@ -343,20 +856,41 @@ const TOOL_MANIFEST = [
343
856
  type: "string",
344
857
  description: "Optional Godot editor executable override for this invocation.",
345
858
  },
346
- }, ["scenePath"]),
859
+ }, ["scenePath", "animationTreePath"]),
347
860
  outputSchema: outputEnvelopeSchema,
348
861
  outputContract: "GdhEditorOperationResult",
349
862
  },
350
863
  {
351
- name: "editor.resource.read",
352
- summary: "Read requested properties from one Godot resource via editor APIs. Nested arrays/dictionaries are encoded recursively with bounded resource handles.",
864
+ name: "editor.animation.state_machine.node.upsert",
865
+ summary: "Create or update one node in an AnimationTree state-machine graph without automating the editor UI.",
353
866
  targetPathPolicy: "optional",
354
- inputSchema: defineObjectSchema("Editor resource read request.", {
355
- resourcePath: {
867
+ inputSchema: defineObjectSchema("Editor animation state-machine node upsert request.", {
868
+ scenePath: {
356
869
  type: "string",
357
- description: "Required res:// resource path, including ::subresource id when needed.",
870
+ description: "Required res:// path for the saved scene to load.",
871
+ },
872
+ animationTreePath: {
873
+ type: "string",
874
+ description: "Required node path to the AnimationTree.",
875
+ },
876
+ stateName: {
877
+ type: "string",
878
+ description: "Required state-machine node name.",
879
+ },
880
+ nodeType: {
881
+ type: "string",
882
+ description: "Optional AnimationNode type. Defaults to AnimationNodeAnimation.",
883
+ },
884
+ position: {
885
+ type: "object",
886
+ description: 'Optional graph position encoded as {"$vector2":{"x":0,"y":0}}.',
887
+ additionalProperties: true,
888
+ },
889
+ properties: {
890
+ type: "object",
891
+ description: 'Optional properties to set on the AnimationNode, for example {"animation":"idle"}.',
892
+ additionalProperties: true,
358
893
  },
359
- propertyNames: stringArraySchema("Optional exact property names to read from the resource."),
360
894
  mode: {
361
895
  type: "string",
362
896
  description: "Optional session mode: auto, headless_only, adopt_only, or disabled.",
@@ -369,26 +903,26 @@ const TOOL_MANIFEST = [
369
903
  type: "string",
370
904
  description: "Optional Godot editor executable override for this invocation.",
371
905
  },
372
- }, ["resourcePath"]),
906
+ }, ["scenePath", "animationTreePath", "stateName"]),
373
907
  outputSchema: outputEnvelopeSchema,
374
908
  outputContract: "GdhEditorOperationResult",
375
909
  },
376
910
  {
377
- name: "editor.resource.properties",
378
- summary: "Discover bounded property metadata for one Godot resource before choosing propertyNames for editor.resource.read.",
911
+ name: "editor.animation.state_machine.node.delete",
912
+ summary: "Delete one node from an AnimationTree state-machine graph.",
379
913
  targetPathPolicy: "optional",
380
- inputSchema: defineObjectSchema("Editor resource property discovery request.", {
381
- resourcePath: {
914
+ inputSchema: defineObjectSchema("Editor animation state-machine node delete request.", {
915
+ scenePath: {
382
916
  type: "string",
383
- description: "Required res:// resource path, including ::subresource id when needed.",
917
+ description: "Required res:// path for the saved scene to load.",
384
918
  },
385
- propertyQuery: {
919
+ animationTreePath: {
386
920
  type: "string",
387
- description: "Optional substring filter for returned property metadata.",
921
+ description: "Required node path to the AnimationTree.",
388
922
  },
389
- propertyLimit: {
390
- type: "number",
391
- description: "Optional positive limit for returned property metadata.",
923
+ stateName: {
924
+ type: "string",
925
+ description: "Required state-machine node name.",
392
926
  },
393
927
  mode: {
394
928
  type: "string",
@@ -402,30 +936,35 @@ const TOOL_MANIFEST = [
402
936
  type: "string",
403
937
  description: "Optional Godot editor executable override for this invocation.",
404
938
  },
405
- }, ["resourcePath"]),
939
+ }, ["scenePath", "animationTreePath", "stateName"]),
406
940
  outputSchema: outputEnvelopeSchema,
407
941
  outputContract: "GdhEditorOperationResult",
408
942
  },
409
943
  {
410
- name: "editor.class.search",
411
- summary: "Search Godot ClassDB and registered script classes with bounded query controls.",
944
+ name: "editor.animation.state_machine.transition.upsert",
945
+ summary: "Create or replace one transition in an AnimationTree state-machine graph without automating the editor UI.",
412
946
  targetPathPolicy: "optional",
413
- inputSchema: defineObjectSchema("Editor class search request.", {
414
- query: {
947
+ inputSchema: defineObjectSchema("Editor animation state-machine transition upsert request.", {
948
+ scenePath: {
415
949
  type: "string",
416
- description: "Optional substring query for class names.",
950
+ description: "Required res:// path for the saved scene to load.",
417
951
  },
418
- baseClass: {
952
+ animationTreePath: {
419
953
  type: "string",
420
- description: "Optional base class filter.",
954
+ description: "Required node path to the AnimationTree.",
421
955
  },
422
- instantiableOnly: {
423
- type: "boolean",
424
- description: "When false, include non-instantiable classes. Defaults to true.",
956
+ fromState: {
957
+ type: "string",
958
+ description: "Required source state name.",
425
959
  },
426
- limit: {
427
- type: "number",
428
- description: "Optional positive result limit.",
960
+ toState: {
961
+ type: "string",
962
+ description: "Required target state name.",
963
+ },
964
+ properties: {
965
+ type: "object",
966
+ description: "Optional properties to set on the transition resource.",
967
+ additionalProperties: true,
429
968
  },
430
969
  mode: {
431
970
  type: "string",
@@ -439,30 +978,30 @@ const TOOL_MANIFEST = [
439
978
  type: "string",
440
979
  description: "Optional Godot editor executable override for this invocation.",
441
980
  },
442
- }),
981
+ }, ["scenePath", "animationTreePath", "fromState", "toState"]),
443
982
  outputSchema: outputEnvelopeSchema,
444
983
  outputContract: "GdhEditorOperationResult",
445
984
  },
446
985
  {
447
- name: "editor.class.info",
448
- summary: "Return bounded property metadata for one Godot or registered script class.",
986
+ name: "editor.animation.state_machine.transition.delete",
987
+ summary: "Delete one transition from an AnimationTree state-machine graph.",
449
988
  targetPathPolicy: "optional",
450
- inputSchema: defineObjectSchema("Editor class info request.", {
451
- className: {
989
+ inputSchema: defineObjectSchema("Editor animation state-machine transition delete request.", {
990
+ scenePath: {
452
991
  type: "string",
453
- description: "Required Godot or registered script class name.",
992
+ description: "Required res:// path for the saved scene to load.",
454
993
  },
455
- includeInherited: {
456
- type: "boolean",
457
- description: "When true, include inherited properties. Defaults to true.",
994
+ animationTreePath: {
995
+ type: "string",
996
+ description: "Required node path to the AnimationTree.",
458
997
  },
459
- propertyQuery: {
998
+ fromState: {
460
999
  type: "string",
461
- description: "Optional substring filter for returned property metadata.",
1000
+ description: "Required source state name.",
462
1001
  },
463
- propertyLimit: {
464
- type: "number",
465
- description: "Optional positive property metadata limit.",
1002
+ toState: {
1003
+ type: "string",
1004
+ description: "Required target state name.",
466
1005
  },
467
1006
  mode: {
468
1007
  type: "string",
@@ -476,13 +1015,13 @@ const TOOL_MANIFEST = [
476
1015
  type: "string",
477
1016
  description: "Optional Godot editor executable override for this invocation.",
478
1017
  },
479
- }, ["className"]),
1018
+ }, ["scenePath", "animationTreePath", "fromState", "toState"]),
480
1019
  outputSchema: outputEnvelopeSchema,
481
1020
  outputContract: "GdhEditorOperationResult",
482
1021
  },
483
1022
  {
484
1023
  name: "editor.operation.run",
485
- summary: "Advanced escape hatch for one targeted Godot-native editor operation. Prefer focused editor.* tools for normal reads. Payloads dispatch on operation.kind, not type.",
1024
+ summary: "Advanced escape hatch for one targeted Godot-native editor operation. Prefer focused editor.* tools for normal reads and common writes. Payloads dispatch on operation.kind, not type.",
486
1025
  targetPathPolicy: "optional",
487
1026
  inputSchema: defineObjectSchema("Editor Bridge operation request.", {
488
1027
  operation: {
@@ -516,69 +1055,15 @@ const TOOL_MANIFEST = [
516
1055
  stringArraySchema,
517
1056
  stringMapSchema,
518
1057
  }),
519
- {
520
- name: "verify.run",
521
- summary: "Execute one verification scenario over a run configuration and return the structured run record.",
522
- targetPathPolicy: "optional",
523
- inputSchema: defineObjectSchema("Runtime verification execution request.", {
524
- runConfigurationId: {
525
- type: "string",
526
- description: "Run configuration id to execute.",
527
- },
528
- verificationScenarioId: {
529
- type: "string",
530
- description: "Verification scenario id to run over the selected run configuration.",
531
- },
532
- provider: {
533
- type: "string",
534
- description: "Optional provider id to force when a run configuration supports multiple providers.",
535
- },
536
- parameters: stringMapSchema("Optional parameter overrides keyed by parameter id."),
537
- enabledFeatures: stringArraySchema("Feature flag ids to force on for this verification run."),
538
- disabledFeatures: stringArraySchema("Feature flag ids to force off for this verification run."),
539
- environment: stringMapSchema("Environment variable overrides keyed by variable name."),
540
- liveWorkspace: {
541
- type: "boolean",
542
- description: "When true, execute against the live workspace instead of an isolated copy.",
543
- },
544
- renderedScreenshot: {
545
- type: "boolean",
546
- description: "When true, request one rendered screenshot artifact for this verification run when the selected runtime path can capture it.",
547
- },
548
- }, ["runConfigurationId", "verificationScenarioId"]),
549
- outputSchema: outputEnvelopeSchema,
550
- outputContract: "GdhRuntimeRunBundle",
551
- },
552
1058
  ];
553
1059
  const RESOURCE_MANIFEST = [
554
1060
  {
555
- name: "run-configurations",
556
- summary: "Read the discovered run configurations for the bound target as structured JSON.",
557
- mimeType: "application/json",
558
- uri: "gdh://run-configurations",
559
- uriTemplate: null,
560
- },
561
- {
562
- name: "run-configuration",
563
- summary: "Read one run configuration for the bound target by id as structured JSON.",
564
- mimeType: "application/json",
565
- uri: null,
566
- uriTemplate: "gdh://run-configurations/{id}",
567
- },
568
- {
569
- name: "verification-scenarios",
570
- summary: "Read the discovered verification scenarios for the bound target as structured JSON.",
571
- mimeType: "application/json",
572
- uri: "gdh://verification-scenarios",
1061
+ name: "run-game-knowledge",
1062
+ summary: "Read project-owned guidance for how agents should run this Godot target.",
1063
+ mimeType: "text/markdown",
1064
+ uri: "gdh://runtime-knowledge/run-game",
573
1065
  uriTemplate: null,
574
1066
  },
575
- {
576
- name: "verification-scenario",
577
- summary: "Read one verification scenario for the bound target by id as structured JSON.",
578
- mimeType: "application/json",
579
- uri: null,
580
- uriTemplate: "gdh://verification-scenarios/{id}",
581
- },
582
1067
  ];
583
1068
  export function listMcpTools() {
584
1069
  return TOOL_MANIFEST;
@@ -757,10 +1242,6 @@ async function invokeMcpToolWithContext(request, context) {
757
1242
  return succeeded(request.toolName, targetPath, await invokeAuthoringDiagnosticsDoctorTool(targetPath));
758
1243
  case "authoring.warmup":
759
1244
  return succeeded(request.toolName, targetPath, await invokeAuthoringWarmupTool(targetPath, request.input));
760
- case "run-config.check":
761
- return succeeded(request.toolName, targetPath, await invokeRunConfigCheckTool(targetPath, request.input));
762
- case "run-config.run":
763
- return succeeded(request.toolName, targetPath, await invokeRunConfigRunTool(targetPath, request.input));
764
1245
  case "editor.session.status":
765
1246
  return succeeded(request.toolName, targetPath, await invokeEditorSessionStatusTool(targetPath, request.input));
766
1247
  case "editor.state":
@@ -807,19 +1288,139 @@ async function invokeMcpToolWithContext(request, context) {
807
1288
  propertyQuery: readOptionalString(request.input["propertyQuery"], "propertyQuery"),
808
1289
  propertyLimit: readOptionalPositiveInteger(request.input["propertyLimit"], "propertyLimit"),
809
1290
  })));
1291
+ case "editor.animation.player.inspect":
1292
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.player.inspect", {
1293
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1294
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1295
+ })));
1296
+ case "editor.animation.clip.read":
1297
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.clip.read", {
1298
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1299
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1300
+ libraryName: readOptionalString(request.input["libraryName"], "libraryName"),
1301
+ animationName: requireString(request.input["animationName"], "animationName"),
1302
+ includeKeys: readOptionalBoolean(request.input["includeKeys"], "includeKeys"),
1303
+ keyLimit: readOptionalPositiveInteger(request.input["keyLimit"], "keyLimit"),
1304
+ })));
1305
+ case "editor.animation.clip.create":
1306
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.clip.create", {
1307
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1308
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1309
+ libraryName: readOptionalString(request.input["libraryName"], "libraryName"),
1310
+ animationName: requireString(request.input["animationName"], "animationName"),
1311
+ length: readOptionalNumber(request.input["length"], "length"),
1312
+ step: readOptionalNumber(request.input["step"], "step"),
1313
+ loopMode: readOptionalStringOrNumber(request.input["loopMode"], "loopMode"),
1314
+ overwrite: readOptionalBoolean(request.input["overwrite"], "overwrite"),
1315
+ })));
1316
+ case "editor.animation.clip.delete":
1317
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.clip.delete", {
1318
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1319
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1320
+ libraryName: readOptionalString(request.input["libraryName"], "libraryName"),
1321
+ animationName: requireString(request.input["animationName"], "animationName"),
1322
+ })));
1323
+ case "editor.animation.track.add":
1324
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.track.add", {
1325
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1326
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1327
+ libraryName: readOptionalString(request.input["libraryName"], "libraryName"),
1328
+ animationName: requireString(request.input["animationName"], "animationName"),
1329
+ trackType: readOptionalStringOrNumber(request.input["trackType"], "trackType") ?? "value",
1330
+ trackPath: requireString(request.input["trackPath"], "trackPath"),
1331
+ atPosition: readOptionalNumber(request.input["atPosition"], "atPosition"),
1332
+ interpolation: readOptionalStringOrNumber(request.input["interpolation"], "interpolation"),
1333
+ updateMode: readOptionalStringOrNumber(request.input["updateMode"], "updateMode"),
1334
+ })));
1335
+ case "editor.animation.track.remove":
1336
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.track.remove", {
1337
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1338
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1339
+ libraryName: readOptionalString(request.input["libraryName"], "libraryName"),
1340
+ animationName: requireString(request.input["animationName"], "animationName"),
1341
+ trackIndex: requireNumber(request.input["trackIndex"], "trackIndex"),
1342
+ })));
1343
+ case "editor.animation.keyframe.upsert":
1344
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.keyframe.upsert", {
1345
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1346
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1347
+ libraryName: readOptionalString(request.input["libraryName"], "libraryName"),
1348
+ animationName: requireString(request.input["animationName"], "animationName"),
1349
+ trackIndex: requireNumber(request.input["trackIndex"], "trackIndex"),
1350
+ time: requireNumber(request.input["time"], "time"),
1351
+ value: requireJsonValue(request.input["value"], "value"),
1352
+ transition: readOptionalNumber(request.input["transition"], "transition"),
1353
+ })));
1354
+ case "editor.animation.keyframe.delete":
1355
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.keyframe.delete", {
1356
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1357
+ animationPlayerPath: requireString(request.input["animationPlayerPath"], "animationPlayerPath"),
1358
+ libraryName: readOptionalString(request.input["libraryName"], "libraryName"),
1359
+ animationName: requireString(request.input["animationName"], "animationName"),
1360
+ trackIndex: requireNumber(request.input["trackIndex"], "trackIndex"),
1361
+ keyIndex: readOptionalNumber(request.input["keyIndex"], "keyIndex"),
1362
+ time: readOptionalNumber(request.input["time"], "time"),
1363
+ })));
1364
+ case "editor.animation.state_machine.create":
1365
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.state_machine.create", {
1366
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1367
+ animationTreePath: requireString(request.input["animationTreePath"], "animationTreePath"),
1368
+ overwrite: readOptionalBoolean(request.input["overwrite"], "overwrite"),
1369
+ properties: request.input["properties"] === undefined
1370
+ ? undefined
1371
+ : requireObject(request.input["properties"], "properties"),
1372
+ })));
1373
+ case "editor.animation.state_machine.read":
1374
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.state_machine.read", {
1375
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1376
+ animationTreePath: requireString(request.input["animationTreePath"], "animationTreePath"),
1377
+ })));
1378
+ case "editor.animation.state_machine.node.upsert":
1379
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.state_machine.node.upsert", {
1380
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1381
+ animationTreePath: requireString(request.input["animationTreePath"], "animationTreePath"),
1382
+ stateName: requireString(request.input["stateName"], "stateName"),
1383
+ nodeType: readOptionalString(request.input["nodeType"], "nodeType"),
1384
+ position: request.input["position"] === undefined
1385
+ ? undefined
1386
+ : requireJsonValue(request.input["position"], "position"),
1387
+ properties: request.input["properties"] === undefined
1388
+ ? undefined
1389
+ : requireObject(request.input["properties"], "properties"),
1390
+ })));
1391
+ case "editor.animation.state_machine.node.delete":
1392
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.state_machine.node.delete", {
1393
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1394
+ animationTreePath: requireString(request.input["animationTreePath"], "animationTreePath"),
1395
+ stateName: requireString(request.input["stateName"], "stateName"),
1396
+ })));
1397
+ case "editor.animation.state_machine.transition.upsert":
1398
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.state_machine.transition.upsert", {
1399
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1400
+ animationTreePath: requireString(request.input["animationTreePath"], "animationTreePath"),
1401
+ fromState: requireString(request.input["fromState"], "fromState"),
1402
+ toState: requireString(request.input["toState"], "toState"),
1403
+ properties: request.input["properties"] === undefined
1404
+ ? undefined
1405
+ : requireObject(request.input["properties"], "properties"),
1406
+ })));
1407
+ case "editor.animation.state_machine.transition.delete":
1408
+ return succeeded(request.toolName, targetPath, await invokeFocusedEditorOperationTool(targetPath, request.input, createEditorOperation("animation.state_machine.transition.delete", {
1409
+ scenePath: requireString(request.input["scenePath"], "scenePath"),
1410
+ animationTreePath: requireString(request.input["animationTreePath"], "animationTreePath"),
1411
+ fromState: requireString(request.input["fromState"], "fromState"),
1412
+ toState: requireString(request.input["toState"], "toState"),
1413
+ })));
810
1414
  case "editor.operation.run":
811
1415
  return succeeded(request.toolName, targetPath, await invokeEditorOperationRunTool(targetPath, request.input));
812
- case "bridge.session.start":
813
- return succeeded(request.toolName, targetPath, await invokeBridgeTool(request.toolName, targetPath, request.input, context));
814
1416
  case "bridge.entry.list":
815
1417
  return succeeded(request.toolName, targetPath, await invokeBridgeTool(request.toolName, targetPath, request.input, context));
816
1418
  case "bridge.entry.invoke":
817
1419
  return succeeded(request.toolName, targetPath, await invokeBridgeTool(request.toolName, targetPath, request.input, context));
818
1420
  case "bridge.session.stop":
819
1421
  return succeeded(request.toolName, targetPath, await invokeBridgeTool(request.toolName, targetPath, request.input, context));
820
- case "verify.run":
821
- return succeeded(request.toolName, targetPath, await invokeVerifyRunTool(targetPath, request.input));
822
1422
  }
1423
+ return failed(request.toolName, targetPath, `Unknown GDH MCP tool: ${request.toolName}`);
823
1424
  }
824
1425
  catch (error) {
825
1426
  return failed(request.toolName, targetPath, formatMcpError(error));
@@ -833,75 +1434,26 @@ async function resolveMcpTargetPath(targetPath) {
833
1434
  }
834
1435
  async function listMcpResources(targetPath) {
835
1436
  const effectiveTargetPath = await resolveMcpTargetPath(targetPath);
836
- const projectConfig = await readProjectConfig(effectiveTargetPath);
837
- const [runConfigurations, verificationScenarios] = await Promise.all([
838
- listRuntimeRecipes({ targetPath: effectiveTargetPath, projectConfig }),
839
- listRuntimeScenarios({ targetPath: effectiveTargetPath }),
840
- ]);
841
- const resources = [
842
- {
843
- uri: "gdh://run-configurations",
844
- name: "run-configurations",
845
- description: "Discovered run configurations for the bound target.",
846
- mimeType: "application/json",
847
- },
1437
+ void effectiveTargetPath;
1438
+ return [
848
1439
  {
849
- uri: "gdh://verification-scenarios",
850
- name: "verification-scenarios",
851
- description: "Discovered verification scenarios for the bound target.",
852
- mimeType: "application/json",
1440
+ uri: "gdh://runtime-knowledge/run-game",
1441
+ name: "run-game-knowledge",
1442
+ description: "Project-owned guidance for how agents should run this Godot target.",
1443
+ mimeType: "text/markdown",
853
1444
  },
854
1445
  ];
855
- for (const runConfiguration of runConfigurations.recipes) {
856
- resources.push({
857
- uri: buildRunConfigurationUri(runConfiguration.id),
858
- name: `run-configuration:${runConfiguration.id}`,
859
- description: `Run configuration "${runConfiguration.id}" for the bound target.`,
860
- mimeType: "application/json",
861
- });
862
- }
863
- for (const verificationScenario of verificationScenarios.scenarios) {
864
- resources.push({
865
- uri: buildVerificationScenarioUri(verificationScenario.id),
866
- name: `verification-scenario:${verificationScenario.id}`,
867
- description: `Verification scenario "${verificationScenario.id}" for the bound target.`,
868
- mimeType: "application/json",
869
- });
870
- }
871
- return resources;
872
1446
  }
873
1447
  async function readMcpResource(targetPath, uri) {
874
1448
  const effectiveTargetPath = await resolveMcpTargetPath(targetPath);
875
- const projectConfig = await readProjectConfig(effectiveTargetPath);
876
- if (uri === "gdh://run-configurations") {
877
- return presentPublicRuntimeTerms(await listRuntimeRecipes({ targetPath: effectiveTargetPath, projectConfig }));
878
- }
879
- if (uri === "gdh://verification-scenarios") {
880
- return presentPublicRuntimeTerms(await listRuntimeScenarios({ targetPath: effectiveTargetPath }));
881
- }
882
- const runConfigurationId = parseResourceId(uri, "gdh://run-configurations/");
883
- if (runConfigurationId !== null) {
884
- return presentPublicRuntimeTerms(await showRuntimeRecipe({
885
- targetPath: effectiveTargetPath,
886
- projectConfig,
887
- recipeId: runConfigurationId,
888
- }));
889
- }
890
- const verificationScenarioId = parseResourceId(uri, "gdh://verification-scenarios/");
891
- if (verificationScenarioId !== null) {
892
- return presentPublicRuntimeTerms(await showRuntimeScenario({
893
- targetPath: effectiveTargetPath,
894
- scenarioId: verificationScenarioId,
895
- }));
1449
+ if (uri === "gdh://runtime-knowledge/run-game") {
1450
+ const projectConfig = await readProjectConfig(effectiveTargetPath);
1451
+ const knowledgePath = projectConfig?.runtimeKnowledge.path ?? ".gdh/runtime-knowledge";
1452
+ const runGamePath = path.join(effectiveTargetPath, knowledgePath, "run-game.md");
1453
+ return await fs.readFile(runGamePath, "utf8");
896
1454
  }
897
1455
  throw new Error(`Unknown GDH MCP resource: ${uri}`);
898
1456
  }
899
- function buildRunConfigurationUri(runConfigurationId) {
900
- return `gdh://run-configurations/${encodeURIComponent(runConfigurationId)}`;
901
- }
902
- function buildVerificationScenarioUri(verificationScenarioId) {
903
- return `gdh://verification-scenarios/${encodeURIComponent(verificationScenarioId)}`;
904
- }
905
1457
  function parseResourceId(uri, prefix) {
906
1458
  if (!uri.startsWith(prefix)) {
907
1459
  return null;
@@ -1245,6 +1797,15 @@ function readOptionalString(value, fieldName) {
1245
1797
  }
1246
1798
  return value;
1247
1799
  }
1800
+ function readOptionalStringOrNumber(value, fieldName) {
1801
+ if (value === undefined || value === null) {
1802
+ return undefined;
1803
+ }
1804
+ if (typeof value !== "string" && typeof value !== "number") {
1805
+ throw new Error(`MCP input field "${fieldName}" must be a string or number.`);
1806
+ }
1807
+ return value;
1808
+ }
1248
1809
  function readOptionalEditorSessionMode(value, fieldName) {
1249
1810
  const mode = readOptionalString(value, fieldName);
1250
1811
  if (mode === null || mode === "auto")
@@ -1264,6 +1825,28 @@ function requireString(value, fieldName) {
1264
1825
  }
1265
1826
  return stringValue;
1266
1827
  }
1828
+ function readOptionalNumber(value, fieldName) {
1829
+ if (value === undefined) {
1830
+ return undefined;
1831
+ }
1832
+ if (typeof value !== "number") {
1833
+ throw new Error(`MCP input field "${fieldName}" must be a number.`);
1834
+ }
1835
+ return value;
1836
+ }
1837
+ function requireNumber(value, fieldName) {
1838
+ const numberValue = readOptionalNumber(value, fieldName);
1839
+ if (numberValue === undefined) {
1840
+ throw new Error(`MCP input field "${fieldName}" is required.`);
1841
+ }
1842
+ return numberValue;
1843
+ }
1844
+ function requireJsonValue(value, fieldName) {
1845
+ if (value === undefined) {
1846
+ throw new Error(`MCP input field "${fieldName}" is required.`);
1847
+ }
1848
+ return value;
1849
+ }
1267
1850
  function requireObject(value, fieldName) {
1268
1851
  if (value === undefined) {
1269
1852
  throw new Error(`MCP input field "${fieldName}" is required.`);