HexaSync Templates — VS Code ExtensionA domain-specific IDE for HexaSync Template development: semantic IntelliSense, reference validation, component navigation, composition and visualization over the HexaSync template graph. What it gives youOpen any
Forty-three commands, all prefixed Requirements
Each dependency is classified mandatory, optional or bundled in Today there is exactly one dependency, and it is mandatory. The In a browser (
|
| In a browser | |
|---|---|
| IntelliSense, diagnostics, navigation, pickers, the Components tree | slower — reading the workspace goes through the editor filesystem API, measured about 4,000× slower than Node on a desktop host, so a large project takes proportionally longer to index |
| Compose Current Project | slower, for the same reason: it composes from the same reads |
| Install / Update IntelliSense | writes 77 files through the editor API. A workspace opened read-only — a repository through vscode.dev, for instance — cannot be written to at all, and the command says so, naming the environment |
| Preview, Dependency Graph, Explain This Value, compose report, rename, variable authoring | identical |
HexaSync: Show Info prints the same list for the host you are actually on. It is generated from one inventory
(src/lib/webCapabilities.ts), so this table and that output cannot drift apart.
Not benchmarked in a browser. The figures above are desktop measurements of the API a browser is obliged to use. No web budget is claimed until one is measured in a real browser host; see ADR 0015 for what would close that.
Develop
git clone <this repo> && cd hexasync-templates-vscode-ext
git clone <the CLI repo> ../hexasync-cli-nodejs # required — see Requirements
corepack yarn install # yarn comes from the packageManager field
corepack yarn build:local # builds BOTH entry points
corepack yarn test # vitest
corepack yarn typecheck # tsc --noEmit
build:local, not build. They differ by one flag, and build resolves @beehexa/* from a registry —
where nothing is published yet, so it fails naming the flag. Every :local script is the one that works today;
the plain one is the path that starts working at the first publish. Same split as package / package:local.
Then press F5 — "Run Extension (Node host)" or "Run Extension (Web host)" — to launch an Extension
Development Host. Its pre-launch task is the default build task, which runs build:local, so F5 works from a
clean clone with the sibling present. corepack yarn watch:local rebuilds on change.
Verify it loaded: HexaSync: Show Extension Info in the Command Palette.
Debug
Two launch configurations in .vscode/launch.json, and which one you pick is a real choice: the web host
has no Node builtins, so a defect that only appears there is usually an import that should not exist. Test in
the Node host, reproduce host-specific bugs in the web host.
corepack yarn watch:local # rebuild on save; reload the dev host window to pick it up
- Breakpoints work in
src/**directly — both bundles are built with source maps (--productionstrips them, andyarn package:localuses--production, so debug an unminifiedyarn build:localinstead). - The log first. Open View → Output → HexaSync Templates. Every subsystem reports there with timings: project discovery, the per-project graph build, validation, tree expansion, reveal. Most "it's slow" and "nothing happened" questions are answered by that channel in one line, and the timings are why three performance defects were found by measurement rather than guesswork.
- Reload after installing a build. An installed
.vsixis only picked up by a window reload (Developer: Reload Window), which is also whyyarn install:vscode-server 0.0.31takes an optional version: bumping it makes the host list a new folder, so you can see at a glance which build is loaded. - A stale graph is marked, not hidden. If the tree or a hover says provisional, the project could not be rebuilt from the current text and you are reading the last good snapshot — look for the parse error.
Package and install
corepack yarn package:local # produces the .vsix alone, resolving @beehexa/* from source
corepack yarn install:vscode-server # browser VS Code: build, package, install, verify
corepack yarn install:vscode # desktop VS Code
corepack yarn install:vscode-server 0.0.31 # …packaged AS that version, without editing package.json
Use package:local today, not package. They differ by one flag — package builds in registry mode, and
nothing under @beehexa/* is published yet, so it fails with a message naming the flag (see Working against
local packages below). package is the release path and starts working at the first publish; package:local
is the one that produces a .vsix right now. Both run the same three gates.
The two install:* scripts build with --local, pick the right CLI, and check the host actually registered
the extension — see Install the extension.
To install a .vsix by hand, which is all either script ultimately does:
# Desktop VS Code
code --install-extension hexasync-templates-<version>.vsix
# Browser VS Code — use the CLI belonging to the RUNNING server, not a `code-server` on PATH
~/.vscode/cli/serve-web/<build>/bin/code-server --install-extension hexasync-templates-<version>.vsix
Mind the version. package:local names the .vsix after package.json, which is still 0.0.1. If you
have previously installed an ad-hoc build (yarn install:vscode-server 0.0.38), the CLI treats 0.0.1 as a
downgrade, refuses it, and still exits 0 — reported success, and the old build still loaded after a reload.
Pass a higher version (yarn install:vscode-server 0.0.39) or uninstall first.
That second path is the whole reason yarn install:vscode-server exists. Installing with the wrong CLI
writes into a directory the running server never reads, and reports success — the machine this was first
written against had five serve-web builds and a code on PATH older than the running server. The script
finds the right one, installs, and then verifies the host actually registered the extension; by hand, you are
picking that build yourself.
Either way the extension is read from the file on disk. No Marketplace access is required at any step — nothing in the install path resolves a registry. Marketplace publication is prepared but deliberately not a precondition for internal adoption.
Then Developer: Reload Window. No CLI can do that part.
Validate dependencies
Three kinds of dependency, checked in three different places, because they fail in three different ways.
1. Editor extensions the developer must install. Declared in package.json under
hexasync.requiredExtensions, each with a minimum version, a mandatory | optional | bundled
classification and a reason:
{ "id": "redhat.vscode-yaml", "minVersion": "1.22.0", "classification": "mandatory",
"reason": "Renders every schema description. Below 1.22.0 it drops markdownDescription beside a $ref…",
// Required on an OPTIONAL entry: the capability a developer loses without it. A reason explains a
// mechanism; this names the thing that stops working, which is what the notice says.
"capability": "Schema descriptions on hover and in completion" }
The extension checks these on activation and reports what is missing or too old, with the reason — the alternative being hovers that are silently empty. Add a dependency by adding an entry; no code changes.
2. Node builtins that must never reach the web host. The web extension host provides no fs,
child_process, path, process, os, util or url:
corepack yarn check:web-bundle # audits the emitted bundle AND every source file
Two guards, because one is not enough: an esbuild plugin fails the web build on such an import and names the
importing file, and check:web-bundle audits the emitted bundle, so a builtin arriving through a dependency
is caught too. Node-only modules are exempted by an explicit allowlist, each justified by a test.
3. The shared @beehexa/* packages. See below — resolution is a build flag, never ambient.
corepack yarn check:assets # the asset bundle integer was bumped if assets/ changed
corepack yarn prose:check # schema descriptions match their authored Markdown source
corepack yarn coverage:check # coverage has not regressed against the recorded baseline
corepack yarn package:local runs the production build and then four gates — the web-bundle audit, the
prose check, the coverage check and the asset-version check — before it will produce a .vsix, so a broken
bundle cannot be packaged by accident.
Two entry points, one build graph
main → dist/extension.node.js · browser → dist/extension.browser.js
Both delegate to src/extension.shared.ts, so behaviour cannot drift between hosts.
Working against local @beehexa/* packages
No publish step is needed to iterate on the shared composer packages — pass --local:
corepack yarn build --local # @beehexa/* resolve from ../hexasync-cli-nodejs
corepack yarn dev --local # same, in watch mode
corepack yarn build:prod --local
corepack yarn build # no flag → published versions
The flag is the only input. There is no persisted mode and no auto-detection: both were tried and replaced. A persisted mode put the answer to "what am I building against?" in a file you last touched days ago; auto-detection meant merely having a sibling checkout on disk silently changed what your build contained. A flag is visible in the command, in shell history and in CI logs.
If your checkout is not the conventional sibling, point at it:
HEXASYNC_CLI_PATH=../elsewhere/hexasync-cli-nodejs corepack yarn build --local
Nothing under @beehexa/* is published yet, so yarn build without the flag fails today —
deliberately, with a message naming the flag. Registry mode starts working when the publish story
runs.
Why @beehexa/hexasync-template-assets is not in dependencies yet: it is not published. Declaring
an unpublishable version breaks install, and declaring a link: path would create a second
resolution mechanism competing with the flag. Until publication, resolution comes from the
bundler plugin (--local) and tsconfig paths (editor-time types). The dependencies entry
lands with the first publish.
Note: the bundler is given
tsconfig.jsonwithpathsstripped. esbuild honourspaths, which would resolve@beehexa/*to local sources regardless of the flag — making the flag decorative and silently mixing the two. Only the plugin decides.pathsremains fortscand the editor, where resolving to local sources is intended.
Release versioning
Four streams, each with one owner. Confusing them is how a consumer ends up running something nobody intended.
| Stream | Format | Owner | Meaning |
|---|---|---|---|
| Extension | semver (package.json) |
this repo | What a developer installs |
@beehexa/* packages |
semver | hexasync-cli-nodejs |
The shared composer library |
| Compose contract | single integer | hexasync-cli-nodejs |
Bumped only when merge, inheritance or substitution semantics change. Semver cannot express this, so a dedicated integer does |
| Asset bundle | single integer (assets/BUNDLE_VERSION) |
this repo | Assets expose no API to break, so ordering is the only question: is my copy older than what is available? |
To cut a release of the extension:
- Bump
versioninpackage.json— the extension stream, and the only one a release touches by default. - Bump
assets/BUNDLE_VERSIONif anything underassets/changed, because a consumer comparing integers is the only way they can tell their copy is behind.yarn check:assetsis now part ofpackageandpackage:local, so a forgotten bump stops the build — but note what it compares:origin/main...HEAD, so it sees committed asset changes, not uncommitted ones. There is no CI in this repository yet; this gate is the enforcement. - Leave the compose contract alone unless composition semantics changed — it is stamped into every published diagnostic set, and bumping it for a refactor makes every consumer think their graph is stale.
corepack yarn package:localand attach the.vsix—packageis the registry-mode path and does not build today, exactly as under Package and install above.
For a throwaway build, yarn install:vscode-server <version> packages as that version without editing
package.json — useful for confirming which build a host has loaded, and never a release.
Stay up to date
Nobody should file a bug against a build from three weeks ago, so the extension can tell you when a newer one exists. It is off until you point it somewhere:
// settings.json — a URL, or a workspace-reachable path
"hexasync.updateManifest": "https://internal.example/hexasync-templates/manifest.json"
The manifest is JSON. version is required; the rest is optional and unknown fields are ignored:
{ "version": "0.0.40", "from": "https://internal.example/releases", "notes": "Flow diagram fixes." }
from is what the notice tells you to do next — only whoever publishes the build knows how it is distributed,
so the extension quotes it rather than inventing one.
Empty by default, deliberately. The manifest's hosting location is not yet decided, so nothing is guessed: with no value set, nothing is read and nothing is shown. When it IS set, every failure is silent — a missing file, an unreachable host, an HTML error page and a half-written manifest all produce a line in the HexaSync Templates output channel and nothing on screen. Dismissing a notice silences that version only; the next build speaks again.
The check reads and nothing more: a bare GET with no query string, no body and no header identifying you, your editor or your workspace (NFR-20). There is no telemetry in this extension.
Layout
src/
extension.node.ts # "main" — Node host entry
extension.browser.ts # "browser" — web host entry
extension.shared.ts # host-agnostic activation
lib/ # pure, testable, no vscode and no Node builtins
adapters/ # VS Code adapters
assets/
intellisense/ # canonical schemas — THIS repo is upstream
docs/ # long-form Markdown, source for hovers and guides
scripts/ # build and CI tooling (Node-only, never bundled)
docs/decisions/ # why this repository looks like this
test/ # vitest; `*Budget.spec.ts` run serialised, see decision 0008
This repository holds VS Code adapters only, no domain logic — the semantic model lives in the @beehexa/* packages so it can also serve the CLI and CI.