diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..b4fa45b
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,50 @@
+name: Release
+
+on:
+ push:
+ tags: ["v*"]
+
+permissions:
+ contents: write
+
+jobs:
+ release:
+ name: Build and publish GitHub release
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - uses: actions/setup-node@v7
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci
+ - name: Check tag matches package.json version
+ run: |
+ version="$(node -p "require('./package.json').version")"
+ if [ "${GITHUB_REF_NAME}" != "v${version}" ]; then
+ echo "Tag ${GITHUB_REF_NAME} does not match package.json version ${version}"
+ exit 1
+ fi
+ - name: Run all checks
+ run: xvfb-run -a npm run check
+ - name: Package VSIX
+ run: npx --yes @vscode/vsce package
+ - name: Extract release notes from CHANGELOG.md
+ run: |
+ awk -v v="${GITHUB_REF_NAME#v}" '
+ $0 ~ "^## \\[" v "\\]" { f = 1; next }
+ /^## \[/ { f = 0 }
+ /^\[[^]]+\]: / { next }
+ f
+ ' CHANGELOG.md > release-notes.md
+ cat release-notes.md
+ - name: Create GitHub release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ prerelease=""
+ case "${GITHUB_REF_NAME}" in v0.*) prerelease="--prerelease" ;; esac
+ gh release create "${GITHUB_REF_NAME}" *.vsix \
+ --title "${GITHUB_REF_NAME}" \
+ --notes-file release-notes.md \
+ ${prerelease}
diff --git a/.vscodeignore b/.vscodeignore
index 93675f5..b0aedae 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -10,3 +10,8 @@ eslint.config.mjs
.vscode-test.mjs
tsconfig.json
CLAUDE.md
+coverage/**
+scripts/**
+.github/**
+.prettierrc.json
+.prettierignore
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f04cceb
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,21 @@
+# Changelog
+
+All notable changes to this project are documented here. The format is based on
+[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project follows
+[Semantic Versioning](https://semver.org/).
+
+## [0.1.0] - 2026-10-05
+
+First preview release. Sets up the extension and project tooling; audio capture and
+transcription are not implemented yet.
+
+### Added
+
+- **Whisper Code: Toggle Dictation** command, bound to Cmd+Alt+D
+ (macOS) / Ctrl+Alt+D (Windows/Linux).
+- Status bar item that shows whether dictation is on and toggles it when clicked.
+- Design spec [0001: Overview](docs/specs/0001-overview.md).
+- CI: formatting, type-aware linting, type checking, tests with a 95% coverage gate,
+ VSIX packaging, and CodeQL analysis.
+
+[0.1.0]: https://github.com/dimastatz/whisper-code/releases/tag/v0.1.0
diff --git a/README.md b/README.md
index bfd5162..9491b4b 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,12 @@ npm install
Then open the folder in VS Code and press **F5**. A new **Extension Development Host**
window opens with the extension loaded; try the shortcut there.
-### Install as a VSIX
+### Install a release
+
+Download the `.vsix` from the [latest release](https://github.com/dimastatz/whisper-code/releases)
+and run `code --install-extension whisper-code-.vsix`.
+
+### Build and install a VSIX
```sh
npx @vscode/vsce package # produces whisper-code-.vsix
diff --git a/package-lock.json b/package-lock.json
index f5da5b5..e494420 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "whisper-code",
- "version": "0.0.1",
+ "version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "whisper-code",
- "version": "0.0.1",
+ "version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@types/mocha": "^10.0.10",
diff --git a/package.json b/package.json
index 0a9aa8f..f5cacdd 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "whisper-code",
"displayName": "Whisper Code",
"description": "Real-time voice dictation for VS Code, powered by local Whisper.",
- "version": "0.0.1",
+ "version": "0.1.0",
"publisher": "dimastatz",
"license": "MIT",
"repository": {