diff --git a/README.md b/README.md
index 5dee23b..7a5521c 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ are regenerated on each release — patches cannot be merged directly.
Report problems (or send patches) to the author; accepted changes are
applied to the development tree and appear in a following snapshot.
-This snapshot was assembled from development commit `eb5baf9cbe59`.
+This snapshot was assembled from development commit `83e472a96e36`.
## License
diff --git a/doc/edit/vscode/README.md b/doc/edit/vscode/README.md
index 472a0d2..26e8786 100644
--- a/doc/edit/vscode/README.md
+++ b/doc/edit/vscode/README.md
@@ -31,15 +31,21 @@ language's structure, everywhere.
## Install
-Copy this `vscode/` directory into your VS Code extensions folder:
+Package the extension as a `.vsix` and install it (the builder needs only
+bash and python3; the editing zip from the Klammertext website ships a
+prebuilt `klammertext.vsix`, so there the first step is already done):
- cp -R vscode ~/.vscode/extensions/klammertext
+ ./make_vsix.sh
+ code --install-extension klammertext.vsix
-then restart VS Code (or run the **Developer: Reload Window** command). If
-you copy the directory out of the Klammertext tree, also copy
-`shared/klammertext_ls.py` and `shared/klammertext_edit.py` into the copied
-folder — or set `klammertext.serverPath`. The editing zip from the
-Klammertext website ships the vendored copies already in place.
+then restart VS Code (or run the **Developer: Reload Window** command).
+`make_vsix.sh` vendors the shared core and the language server into the
+package automatically, from this folder or from `../shared/`.
+
+Do **not** copy this directory into `~/.vscode/extensions/` by hand:
+modern VS Code loads only *registered* extensions, so a merely copied
+folder is silently ignored (and flagged for deletion in that directory's
+`.obsolete` file). Installing the `.vsix` is what registers it.
**Note:** `.kt` is also Kotlin's extension. Stock VS Code has no Kotlin
support built in, so there is no conflict out of the box; if you install a
diff --git a/doc/edit/vscode/make_vsix.sh b/doc/edit/vscode/make_vsix.sh
new file mode 100755
index 0000000..f5f2a5c
--- /dev/null
+++ b/doc/edit/vscode/make_vsix.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+# make_vsix.sh — package the Klammertext VS Code extension as klammertext.vsix.
+#
+# Modern VS Code loads only REGISTERED extensions: a folder merely copied
+# into ~/.vscode/extensions/ is ignored (and flagged in its .obsolete file),
+# so the extension must be installed as a .vsix:
+#
+# ./make_vsix.sh
+# code --install-extension klammertext.vsix
+#
+# A .vsix is a zip holding a two-file manifest plus the extension payload
+# under extension/. This script builds it with bash + python3 only — no
+# npm, no vsce — matching the extension's no-dependency design. The shared
+# core and language server are vendored into the payload: from this folder
+# if present (the editing-zip layout), else from ../shared (the repository
+# layout).
+
+set -e
+cd "$(dirname "$0")"
+
+OUT="$PWD/klammertext.vsix"
+STAGE="$(mktemp -d)"
+trap 'rm -rf "$STAGE"' EXIT
+
+mkdir -p "$STAGE/extension/syntaxes"
+cp package.json extension.js language-configuration.json README.md "$STAGE/extension/"
+cp syntaxes/klammertext.tmLanguage.json "$STAGE/extension/syntaxes/"
+for f in klammertext_edit.py klammertext_ls.py; do
+ if [ -f "$f" ]; then
+ cp "$f" "$STAGE/extension/"
+ elif [ -f "../shared/$f" ]; then
+ cp "../shared/$f" "$STAGE/extension/"
+ else
+ echo "make_vsix.sh: cannot find $f (looked here and in ../shared)" >&2
+ exit 1
+ fi
+done
+
+VERSION=$(python3 -c "import json; print(json.load(open('package.json'))['version'])")
+
+cat > "$STAGE/[Content_Types].xml" <<'EOF'
+
+
+
+
+
+
+
+
+EOF
+
+cat > "$STAGE/extension.vsixmanifest" <
+
+
+
+ Klammertext
+ Klammertext language support: syntax highlighting, delimiter matching, structural reindentation, table alignment, and delimiter diagnostics.
+ Programming Languages
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+EOF
+
+python3 - "$STAGE" "$OUT" <<'EOF'
+import os, sys, zipfile
+stage, out = sys.argv[1], sys.argv[2]
+with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z:
+ for root, _dirs, files in os.walk(stage):
+ for f in sorted(files):
+ full = os.path.join(root, f)
+ z.write(full, os.path.relpath(full, stage))
+EOF
+
+echo "Created $OUT"
+echo "Install with: code --install-extension $OUT"
diff --git a/tst/editor_test.sh b/tst/editor_test.sh
index 479bf55..11d6305 100755
--- a/tst/editor_test.sh
+++ b/tst/editor_test.sh
@@ -211,6 +211,28 @@ else
echo "(no Node runtime — the VS Code extension test is skipped; the language server is still tested)"
fi
+# --- the VS Code .vsix package builds with the full payload ----------------
+# (the no-marketplace install path; needs only bash + python3)
+mkdir -p "$OUT/vsix"
+cp -R "$EDIT_DIR/vscode" "$EDIT_DIR/shared" "$OUT/vsix/"
+if ( cd "$OUT/vsix/vscode" && ./make_vsix.sh >/dev/null 2>&1 ) && \
+ PYTHONDONTWRITEBYTECODE=1 python3 -c "
+import sys, zipfile
+names = set(zipfile.ZipFile('$OUT/vsix/vscode/klammertext.vsix').namelist())
+need = {'extension.vsixmanifest', '[Content_Types].xml',
+ 'extension/package.json', 'extension/extension.js',
+ 'extension/language-configuration.json', 'extension/README.md',
+ 'extension/klammertext_edit.py', 'extension/klammertext_ls.py',
+ 'extension/syntaxes/klammertext.tmLanguage.json'}
+sys.exit(1 if need - names else 0)
+"; then
+ echo "${green}PASS${reset} vscode .vsix package builds (make_vsix.sh)"
+ PASS=$((PASS + 1))
+else
+ echo "${red}FAIL${reset} vscode .vsix package builds (make_vsix.sh)"
+ FAIL=$((FAIL + 1))
+fi
+
# --- compare ---------------------------------------------------------------
for f in $INDENT_FIXTURES $ALIGN_FIXTURES; do
check "$f (python)" "$OUT/$f.py.out" "$FIX/${f}_expected.kt"