VS Code reference plugin
Install missing Marketplace extensions for a selected VS Code CLI and profile.
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.commandreturns the resolved executable path without launching it.vscode.versioninvokes--versionand uses Etch’s supported numeric version parser on the first nonempty output line. Unsupported suffix formats are errors.vscode.extensionsreturns 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.