Skip to main content

create-extension

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

Informations de source

Dépôt
dylanroscover/Embody
Dernière activité de la source
26 septembre 2026 à 06:32
Langue détectée de SKILL.md
anglais
Étoiles
191
Forks
10

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
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.
Voir sur GitHub