Skip to main content

create-extension

MUST READ before calling create_extension. Required parameters, lifecycle methods, and wiring steps.

Datos de origen

Repositorio
dylanroscover/Embody
Última actividad en el origen
26 de septiembre de 2026 a las 06:32
Idioma detectado de SKILL.md
inglés
Estrellas
182
Forks
11

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
create-extension
description
MUST READ before calling create_extension. Required parameters, lifecycle methods, and wiring steps.
<!-- Generated by Embody/Envoy - Do not remove this comment - sha:6b85e409552f32cb --> # Create Extension Workflow Use the `create_extension` MCP tool to create a fully-wired TD extension: ``` create_extension( parent_path='/path/to/parent', class_name='MyFeatureExt', name='MyFeature', # Optional: COMP name code='...', # Optional: initial Python code promote=True, # Optional: promote extension methods ext_name='MyFeature', # Optional: extension name ext_index=0 # Optional: extension index ) ``` This creates: baseCOMP + text DAT + extension wiring, initialized and ready to use. **Wiring by hand: the Extension Object parameter is a constant-mode Str par whose VALUE is Python** (`par.ext0object.val = "op('./MyFeatureExt').module.MyFeatureExt(me)"` -- the Extensions page is the `ext` sequence on 2025 builds: `ext0object`, `ext0name`, `ext0promote`; there is no `extension1`). Never set its `.expr`: the extension comes back `None` with `extensionsReady` True and `errors()` empty; the only trace is a `SyntaxError ... Context:(Extension 1)` on `scriptErrors()` (`get_op_errors` `kind: 'script'`). ## Extension Lifecycle Methods Always implement these in your extension class: ```python class MyFeatureExt: def __init__(self, ownerComp: COMP) -> None: self.ownerComp = ownerComp def onDestroyTD(self) -> None: """Called on old instance before TD reinitializes. Clean up callbacks, timers, etc.""" pass def onInitTD(self) -> None: """Called at end of frame after init. Safe to access other extensions and cooked network.""" pass ``` ## Naming Convention Extension classes and source DATs must follow the `NameExt` convention: - Class: `MyFeatureExt` - DAT: `MyFeatureExt` (matches class name) ## Three Namespace Tiers TD promotes **every capitalized member** -- methods and class constants alike. There is no per-member opt-out, so the capital letter is the access modifier. Pick the tier by who calls it: | Tier | Name | Reached as | Who calls it | |---|---|---|---| | 1. Public API | `UpperCamelCase` | `op.MyFeature.DoSomething()` | A user or an agent, deliberately | | 2. Wiring | `lowerCamelCase` | `op.MyFeature.ext.MyFeature.onFrame()` | The COMP's own exec / callback / parexec DATs | | 3. Private | `_lowerCamelCase` | inside the class only | The class itself | **Test: could a user or an agent reasonably call this ON the COMP?** If not, it is not tier 1. Promoting a frame hook is a design flaw, not a shortcut. What makes an over-wide tier 1 harmful is concrete, not cosmetic: co-mounted extensions share one namespace and TD documents no precedence for a duplicate, so one name silently wins; and every promoted name -- constants included -- is reachable by any `getattr(comp, name)` dispatcher. Promoted members do NOT appear in `dir()`, so this is not an autocomplete argument. Two traps: - **`.ext.<Name>` resolves by the Extension Name parameter, not the class name.** If `ext_name='MyFeature'` and the class is `MyFeatureExt`, then `op.MyFeature.ext.MyFeature.x()` works and `op.MyFeature.ext.MyFeatureExt.x()` raises. - **`op.MyFeature` requires a Global OP Shortcut, and `create_extension` never sets one.** Straight out of the tool, `op.MyFeature` raises `AttributeError`. Reach the COMP by a relative path (`op('./MyFeature')`, `parent.Host.op('MyFeature')`), or set `par.opshortcut` deliberately -- it is globally unique, so assigning a name already in use silently steals it from the previous holder. Reserve it for genuine project-wide singletons. **Pass `parent_shortcut` when descendants need to reach the COMP.** It sets `par.parentshortcut`, which is what makes `parent.MyFeature` resolve and keeps the COMP's own callback DATs off depth-coupled `parent()` chains: ``` create_extension(parent_path='/project1', class_name='MyFeatureExt', name='MyFeature', ext_name='MyFeature', parent_shortcut='MyFeature') ``` It refuses to overwrite a shortcut the COMP already declares -- re-pointing one silently redirects every reference already using it -- and says so in the result's `warning`. Both shortcuts can also be set by hand: ```python comp.par.parentshortcut = 'MyFeature' # descendants reach it as parent.MyFeature comp.par.opshortcut = 'MyFeature' # project-wide: op.MyFeature -- singletons only ``` **NEVER cache extension references** in variables -- always call inline. Cached refs go stale on reinit. ## Referencing From Inside the Extension Capture the owner once and navigate from it. `self.ownerComp` is rung 1 of the referencing ladder in `td-python.md`; the bare global `parent()` is banned in an extension class body. ```python class MyFeatureExt: def __init__(self, ownerComp: COMP) -> None: self.ownerComp = ownerComp # rung 1 -- everything else hangs off this def rebuild(self): target = self.ownerComp.op('./render1') # own children cfg = self.ownerComp.par.Mode.eval() # own parameters ``` ## Extensions Inside TDXN COMPs If the extension lives inside a TDXN-strategy COMP (or the extension's ownerComp is one), `onInitTD` will fire **before** TDXN import reconstructs the network. Any state set up during `onInitTD` - created operators, parameter values, stored data - is overwritten when `ImportNetwork` runs with `clear_first=True`. **Always defer initialization:** ```python def onInitTD(self): """Defer setup so it runs after TDXN import completes.""" run('args[0].postInit()', self, delayFrames=5) def postInit(self): """Safe to set up state here - TDXN import is complete.""" # Create operators, set parameters, initialize state pass ``` This applies on project open, after every Ctrl+S (strip/restore cycle), and on manual TDXN reimport. The deferred method must be idempotent - it may run multiple times.
Ver en GitHub