diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 84dc9ba..4eeba85 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,32 +1,45 @@
 name: CI
 
 on:
-  pull_request:
-    branches:
-      - "**"
   push:
-    branches:
-      - "master"
+    branches: [master]
+    paths:
+      - grammar.js
+      - src/**
+      - test/**
+      - bindings/**
+      - binding.gyp
+  pull_request:
+    paths:
+      - grammar.js
+      - src/**
+      - test/**
+      - bindings/**
+      - binding.gyp
+
+concurrency:
+  group: ${{github.workflow}}-${{github.ref}}
+  cancel-in-progress: true
+
 jobs:
   test:
-    runs-on: ${{ matrix.os }}
+    name: Test parser
+    runs-on: ${{matrix.os}}
     strategy:
-      fail-fast: true
+      fail-fast: false
       matrix:
-        os: [macos-latest, ubuntu-latest]
+        os: [ubuntu-latest, windows-latest, macos-14]
     steps:
-      - uses: actions/checkout@v4
-      - uses: actions/setup-node@v4
+      - name: Checkout repository
+        uses: actions/checkout@v4
+      - name: Set up tree-sitter
+        uses: tree-sitter/setup-action/cli@v1
+      - name: Run tests
+        uses: tree-sitter/parser-test-action@v2
         with:
-          node-version: 18
-      - run: npm install
-      - run: npm test
-  test_windows:
-    runs-on: windows-latest
-    steps:
-      - uses: actions/checkout@v4
-      - uses: actions/setup-node@v4
+          test-rust: ${{runner.os == 'Linux'}}
+      - name: Parse examples
+        id: examples
+        uses: tree-sitter/parse-action@v4
         with:
-          node-version: 18
-      - run: npm install
-      - run: npm run-script test-windows
+          files: examples/*.css
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 22175e6..f97f543 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -2,21 +2,20 @@ name: Fuzz Parser
 
 on:
   push:
+    branches: [master]
     paths:
       - src/scanner.c
   pull_request:
     paths:
       - src/scanner.c
-  workflow_dispatch:
 
 jobs:
-  test:
-    name: Parser fuzzing
+  fuzz:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v4
-      - uses: vigoux/tree-sitter-fuzz-action@v1
+      - name: Checkout repository
+        uses: actions/checkout@v4
+      - name: Run fuzzer
+        uses: tree-sitter/fuzz-action@v4
         with:
-          language: css
-          external-scanner: src/scanner.c
-          time: 60
+          tree-sitter-version: v0.22.5
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 103e92a..96f1a4d 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -2,18 +2,25 @@ name: Lint
 
 on:
   push:
-    branches:
-      - master
+    branches: [master]
+    paths:
+      - grammar.js
   pull_request:
-    branches:
-      - "**"
+    paths:
+      - grammar.js
 
 jobs:
   lint:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v4
+      - name: Checkout repository
+        uses: actions/checkout@v4
+      - name: Set up Node.js
+        uses: actions/setup-node@v4
+        with:
+          cache: npm
+          node-version: ${{vars.NODE_VERSION}}
       - name: Install modules
-        run: npm install
+        run: npm ci --legacy-peer-deps
       - name: Run ESLint
         run: npm run lint
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..ac31648
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,23 @@
+name: Publish package
+
+on:
+  push:
+    tags: ["*"]
+
+concurrency:
+  group: ${{github.workflow}}-${{github.ref}}
+  cancel-in-progress: true
+
+jobs:
+  npm:
+    uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main
+    secrets:
+      NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
+  crates:
+    uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main
+    secrets:
+      CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_REGISTRY_TOKEN}}
+  pypi:
+    uses: tree-sitter/workflows/.github/workflows/package-pypi.yml@main
+    secrets:
+      PYPI_API_TOKEN: ${{secrets.PYPI_API_TOKEN}}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 870eb84..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,103 +0,0 @@
-name: Release
-
-on:
-  workflow_run:
-    workflows: ["CI"]
-    branches:
-      - master
-    types:
-      - completed
-
-jobs:
-  release:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v4
-        with:
-          fetch-depth: 0
-
-      - name: Get previous commit SHA
-        id: get_previous_commit
-        run: |
-          LATEST_TAG=$(git describe --tags --abbrev=0)
-          if [[ -z "$LATEST_TAG" ]]; then
-            echo "No tag found. Failing..."
-            exit 1
-          fi
-          echo "latest_tag=${LATEST_TAG#v}" >> "$GITHUB_ENV" # Remove 'v' prefix from the tag
-
-      - name: Check if version changed and is greater than the previous
-        id: version_check
-        run: |
-          # Compare the current version with the version from the previous commit
-          PREVIOUS_NPM_VERSION=${{ env.latest_tag }}
-          CURRENT_NPM_VERSION=$(jq -r '.version' package.json)
-          CURRENT_CARGO_VERSION=$(awk -F '"' '/^version/ {print $2}' Cargo.toml)
-          if [[ "$CURRENT_NPM_VERSION" != "$CURRENT_CARGO_VERSION" ]]; then # Cargo.toml and package.json versions must match
-            echo "Mismatch: NPM version ($CURRENT_NPM_VERSION) and Cargo.toml version ($CURRENT_CARGO_VERSION)"
-            echo "version_changed=false" >> "$GITHUB_ENV"
-          else
-            if [[ "$PREVIOUS_NPM_VERSION" ==  "$CURRENT_NPM_VERSION" ]]; then
-              echo "version_changed=" >> "$GITHUB_ENV"
-            else
-              IFS='.' read -ra PREVIOUS_VERSION_PARTS <<< "$PREVIOUS_NPM_VERSION"
-              IFS='.' read -ra CURRENT_VERSION_PARTS <<< "$CURRENT_NPM_VERSION"
-              VERSION_CHANGED=false
-              for i in "${!PREVIOUS_VERSION_PARTS[@]}"; do
-                if [[ ${CURRENT_VERSION_PARTS[i]} -gt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
-                  VERSION_CHANGED=true
-                  break
-                elif [[ ${CURRENT_VERSION_PARTS[i]} -lt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
-                  break
-                fi
-              done
-
-              echo "version_changed=$VERSION_CHANGED" >> "$GITHUB_ENV"
-              echo "current_version=${CURRENT_NPM_VERSION}" >> "$GITHUB_ENV"
-            fi
-          fi
-
-      - name: Display result
-        run: |
-          echo "Version bump detected: ${{ env.version_changed }}"
-
-      - name: Fail if version is lower
-        if: env.version_changed == 'false'
-        run: exit 1
-
-      - name: Setup Node
-        if: env.version_changed == 'true'
-        uses: actions/setup-node@v4
-        with:
-          node-version: 18
-          registry-url: "https://registry.npmjs.org"
-      - name: Publish to NPM
-        if: env.version_changed == 'true'
-        env:
-          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
-        run: npm publish
-
-      - name: Setup Rust
-        if: env.version_changed == 'true'
-        uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: stable
-          override: true
-      - name: Publish to Crates.io
-        if: env.version_changed == 'true'
-        uses: katyo/publish-crates@v2
-        with:
-          registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
-
-      - name: Tag versions
-        if: env.version_changed == 'true'
-        run: |
-          git checkout master
-          git config user.name github-actions[bot]
-          git config user.email github-actions[bot]@users.noreply.github.com
-          git tag -d "v${{ env.current_version }}" || true
-          git push origin --delete "v${{ env.current_version }}" || true
-          git tag -a "v${{ env.current_version }}" -m "Version ${{ env.current_version }}"
-          git push origin "v${{ env.current_version }}"