Skip to content
Browse documentation

VS Code reference plugin

Install missing Marketplace extensions for a selected VS Code CLI and profile.

EtchReference

This bundle supplies the vscode action and vscode.command, vscode.version, and vscode.extensions fact providers. It stays external to Etch core and must be explicitly loaded. It requires Python 3.9+ and the standard library only.

Copy this directory into a consumer as vendor/etch-vscode, or reference it inside a vendored Etch checkout. Declare it in the consumer’s defaults.conf:

{
    "schema_version": 1,
    "plugins": ["vendor/etch-vscode"],
    "defaults": {
        "vscode": {"command": "code", "timeout": 120},
    },
}

The monorepo path is plugins/vscode, following the reference-bundle placement decision. The entrypoint metadata identifies etch-vscode version 0.2.0, plugin API 1. etch plan --verbose shows that origin; core registration does not load or register this bundle.

Configure extensions and observations

Example modules/editor/module.conf:

{
    "schema_version": 1,
    "name": "editor",
    "facts": {
        "editor_command": {"provider": "vscode.command"},
        "editor_version": {"provider": "vscode.version"},
        "editor_extensions": {"provider": "vscode.extensions"},
    },
    "actions": [
        {
            "vscode": {
                "extensions": [
                    "astro-build.astro-vscode",
                    "editorconfig.editorconfig",
                    "ms-python.python",
                ],
            },
            "refresh": ["editor_extensions"],
        },
    ],
}

Run etch plan editor to inspect, then etch apply editor to reconcile. Install VS Code and make its code command available first. On macOS, VS Code documents the Command Palette action for installing that command in PATH. See the official VS Code command-line reference for command availability and extension management flags.

Action options:

Option Behavior
extensions Required list of Marketplace IDs. An empty list requests no installations.
command One executable, default code; absolute or module-relative paths and PATH names are accepted. No shell command strings or extra arguments.
profile Optional VS Code profile name; passed as one argument to listing/install calls. Omission uses the CLI default profile.
timeout Positive finite seconds per CLI invocation, default 120.
retries Integer from 0 to 3, default 0. Additional attempts per missing extension for recognized temporary service errors.

command, profile, timeout and retries accept action defaults. An action’s value replaces that default. Fact configurations accept the same three target options, with their own built-in defaults; they do not inherit action defaults. Keep action and fact targets aligned when using an alternate command or VS Code profile.

Reconciliation and scope

IDs are trimmed, lowercased, deduplicated and sorted. This initial schema accepts publisher.extension components containing letters, digits and hyphens, starting with a letter or digit. VSIX paths, identifiers ending in .vsix, version pins, removal, upgrades, pre-release and force-install options are outside initial scope. Unknown options fail validation. Unlisted extensions are always preserved.

Inspection locates the CLI and reads --list-extensions. A satisfied action plans SKIP; otherwise it lists missing IDs and advertises network use. Application rechecks installed extensions, installs missing IDs in sorted order through --install-extension, and verifies inventory after each installation. This also avoids reinstalling requested members already brought in by an extension pack. It does not parse localized installation-success messages or silently accept an exit-zero response that leaves an extension absent.

By default, a CLI error or timeout fails the action without retry. For bounded recovery, set retries: 2 in the action payload or its defaults. Retries apply only to install failures containing the English CLI message Server returned 429, 502, 503 or 504. Classification uses captured CLI output, not a structured HTTP response. Localized messages, unrecognized transport errors, timeouts, invalid IDs, missing executables, permissions and inventory failures are not retried.

After a recognized error, fresh inventory is read before another attempt. If the extension is already present, it is not installed again. Otherwise retries wait 1, 2 and 4 seconds (depending on the configured count), with an explicit stderr message naming the extension and retry number. At most three additional attempts and seven seconds of backoff are allowed per missing extension. Every CLI call has its own timeout; with three retries the install/reinspection loop can consume up to eight call timeouts plus seven seconds per extension, in addition to initial inventory and planning calls. It is not a whole-action deadline.

The action retains application:vscode while retrying, preventing another VS Code mutation from racing it; unrelated resources can keep progressing. Exhaustion fails the action with its last error. Successfully installed extensions and unlisted extensions remain installed; no rollback or removal occurs. Etch blocks the failed module’s dependent chain while independent modules continue. Reapplying observes real state and installs only what is still missing.

All actions reserve application:vscode, including alternate commands/profiles. This conservative application-wide resource serializes VS Code changes across modules. The provider does not change the process cwd or environment.

Facts and refresh

  • vscode.command returns the resolved executable path without launching it.
  • vscode.version invokes --version and uses Etch’s supported numeric version parser on the first nonempty output line. Unsupported suffix formats are errors.
  • vscode.extensions returns a sorted, unique list of lowercase installed IDs.

If the executable is missing or not executable, facts are UNAVAILABLE; the action fails with guidance to install/expose the CLI. Invalid output and command failures become fact errors through the shared provider lifecycle. Facts are lazy and cached.

Use action-level refresh to name each declared extension fact that must be observed again after changes. Etch validates these references, invalidates observations after successful execution, and gathers them when consumed. The bundle does not guess local fact names or refresh unrelated profiles. Command/version facts normally need refresh after installing VS Code itself, not after adding an extension. Failed actions do not trigger refresh; a subsequent invocation gathers fresh state.

Planning runs inspection probes only and never installs extensions. Subprocesses use argv, closed stdin, module cwd, a timeout, and bounded output reads (1 MiB per stream). Unexpected inventory lines are rejected instead of being treated as extension IDs.

Inventory inspection is shared across actions within one planning snapshot for matching CLI targets (and VS Code profiles). Apply always reads current inventory without this cache. Executable plans retain their intent across unrelated actions; conditions and changed fact inputs still receive reevaluation. No inventory is persisted between snapshots or invocations.