ableton-extensions-skill

内容来源:SKILL.md(标准 Skill 格式) · 原始地址 · 查看安装指南

原始内容


name: ableton-extensions description: Scaffold, write, build, and package Ableton Live extensions with the Ableton Extensions SDK (@ableton-extensions/sdk, TypeScript). Use when creating or editing an Ableton Live extension — adding context-menu actions, modal/progress dialogs, or code that manipulates tracks, clips, MIDI notes, devices, tempo, audio rendering, or selections in a Live Set.

Ableton Extensions SDK

Build tools that run inside Ableton Live. An extension is a Node/TypeScript module bundled to a single CommonJS file that Live's Extension Host loads. It registers commands and wires them to context-menu actions, then reads and mutates the Live Set through a typed object model (tracks, clips, notes, devices, tempo, scenes).

Extensions are an offline editing / automation layer. They are not real-time processors and not devices in the signal chain: they cannot process the live audio or MIDI stream. The SDK has no note-on/off, MIDI-input, or stream callbacks (the only on* callbacks are dialog/registration lifecycle internals) — an extension only acts when the user triggers a command, then mutates the data model. For real-time MIDI/audio transformation (e.g. "play one key, hear a chord"), the right tool is a MIDI/audio effect device in the track chain: Live's built-in MIDI effects (the Chord device does exactly one-note→chord — set Shift voices to e.g. +4, +7) or a Max for Live device — not an Extension. Extensions complement those by transforming clips/notes at edit time, batch-editing the Set, and adding context-menu tools, dialogs, and rendering.

Local SDK location

The SDK distribution lives wherever you unpacked it — referred to below as <sdk-dir>. It contains:

  • docs/ — pre-built HTML documentation (getting-started, essentials, development, design)
  • api/ — TypeDoc HTML API reference
  • examples/ — 7 runnable examples (read these for canonical patterns)
  • ableton-create-extension-<version>.tgz — the scaffolding tool
  • ableton-extensions-cli-<version>.tgz — the build/run/package CLI
  • ableton-extensions-sdk-<version>.tgz — the SDK library

This skill was written against SDK 1.0.0-beta.0 (API version "1.0.0"); newer builds may ship a different <version> — check the actual .tgz filenames in <sdk-dir> and substitute it wherever it appears below. Requires Node.js >= 24.14.1.

Reference files (load as needed)

  • reference/scaffolding.md — create a project, the generated file layout, manifest.json / package.json / build.ts / tsconfig.json, the extensions-cli run|package commands, the .env / EXTENSION_HOST_PATH, debugging, log file locations, packaging to .ablx.
  • reference/api.md — the full object model: ExtensionContext, handles, transactions, Song/Track/Clip/MidiClip/AudioClip/Device/Scene, MIDI NoteDescription, enums (WarpMode, GridQuantization), context-menu scopes, selection types.
  • reference/recipes.md — copy-paste code for every common task: context menus, modal dialogs (with the WebView bridge + Ableton-themed CSS), progress dialogs with cancellation, creating/editing clips and notes, audio import/render, transactions.

The examples/ folder is the source of truth for idiomatic code — prefer reading the closest example to the task over guessing.

The entry point — always this shape

src/extension.ts exports activate. Call initialize once, then register commands and menu actions synchronously:

import { initialize, type ActivationContext } from "@ableton-extensions/sdk";

export function activate(activation: ActivationContext) {
  const context = initialize(activation, "1.0.0"); // ExtensionContext

  // 1. Register a command (the logic)
  context.commands.registerCommand("myext.doThing", (arg: unknown) => {
    // arg is a Handle, ArrangementSelection, or ClipSlotSelection
    // depending on the scope the menu action was registered for
  });

  // 2. Surface it as a right-click menu item (the trigger)
  context.ui.registerContextMenuAction("AudioClip", "Do Thing", "myext.doThing");
}

context (the ExtensionContext) is the root of everything:

  • context.application.song — the Live Set (tempo, tracks, scenes, …)
  • context.commandsregisterCommand(id, cb) / executeCommand(id, …)
  • context.uiregisterContextMenuAction, showModalDialog, withinProgressDialog
  • context.resourcesimportIntoProject(path), render audio from a track
  • context.environmentstorageDirectory, tempDirectory, language
  • context.getObjectFromHandle(handle, Class) — turn a Handle into a typed object
  • context.withinTransaction(() => …) — group mutations into one undo step

Two concepts that trip people up

Handles are not objects. Live passes commands lightweight Handles ({ id: bigint }). Resolve to a typed object with context.getObjectFromHandle(handle, Track). Handles go invalid when the object is deleted, moved in the arrangement, or the Set changes — never cache objects or handles across operations.

Commands receive scope-dependent args. Object scopes ("AudioClip", "MidiTrack", "ClipSlot", "Scene", …) pass a single Handle. Selection scopes pass a structured object: "AudioTrack.ArrangementSelection" / "MidiTrack.ArrangementSelection" pass an ArrangementSelection (time_selection_start, time_selection_end, selected_lanes: Handle[]); "ClipSlotSelection" passes { selected_clip_slots }. Match the scope you register to the arg type you cast.

Standard workflow

  1. Scaffold (new project): npx file:<sdk-dir>/ableton-create-extension-<version>.tgz <dir> — interactive prompts for name, author, Live path, UI support. See reference/scaffolding.md.
  2. Write src/extension.ts — register commands + menu actions; see reference/recipes.md.
  3. Run in Live: npm start (builds with esbuild, then extensions-cli run loads it into the Extension Host). Enable Developer Mode first in Live's Preferences → Extensions.
  4. Debug: console.log/warn/errorExtensionHost.txt (paths in reference/scaffolding.md). In VS Code, F5 / "Debug Extension".
  5. Package: npm run package → a distributable .ablx archive users drop into Live.

Conventions that matter

  • TypeScript, ESM project, CJS bundle. package.json is "type": "module"; build.ts bundles to format: "cjs", platform: "node". Keep it that way — the Extension Host loads CommonJS.
  • Namespace command IDs ("myext.action") to avoid collisions with other extensions.
  • Most mutations are async (Promise). Batch related edits in context.withinTransaction(() => promises) then await Promise.all(...) so the user gets one undo step and Live updates once.
  • Narrow handle types defensively with instanceof (e.g. resolve to Clip, then check clip instanceof AudioClip) — a menu scope can match objects the code must reject.
  • HTML dialogs are inlined by esbuild (loader: { ".html": "text" }) and passed as a data:text/html, URL — there's no web server.