diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..16bec6a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{md,yml,yaml}]
+indent_style = space
+indent_size = 2
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..1c1887e
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @nekrich
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..83f93ac
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,27 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ""
+labels: bug
+assignees: ""
+---
+
+**Describe the bug**
+
+
+
+**Steps to reproduce**
+
+
+
+**Expected behavior**
+
+
+
+**Screenshots**
+
+
+
+**Additional context**
+
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..f52b3b9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,23 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ""
+labels: enhancement
+assignees: ""
+---
+
+**Is your feature request related to a problem? Please describe.**
+
+
+
+**Describe the solution you'd like**
+
+
+
+**Describe alternatives you've considered**
+
+
+
+**Additional context**
+
+
diff --git a/.github/PULL_REQUEST/pull_request_template.md b/.github/PULL_REQUEST/pull_request_template.md
new file mode 100644
index 0000000..a32de42
--- /dev/null
+++ b/.github/PULL_REQUEST/pull_request_template.md
@@ -0,0 +1,34 @@
+
+
+## Description
+
+
+
+## Motivation and Context
+
+
+
+
+## Types of changes
+
+
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to change)
+
+## Usage examples
+
+
+
+## How Has This Been Tested?
+
+
+
+## Checklist
+
+
+
+
+- [ ] I have updated the documentation accordingly.
+- [ ] I have added tests to cover my changes.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..5ace460
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..d21eff6
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,22 @@
+name: Build
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+jobs:
+ plugin_test:
+ name: asdf plugin test
+ strategy:
+ matrix:
+ os:
+ - macos-latest
+ - ubuntu-latest
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: asdf_plugin_test
+ uses: asdf-vm/actions/plugin-test@v3
+ with:
+ command: xcbeautify --help
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..7368930
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,24 @@
+name: Lint
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: asdf-vm/actions/install@v3
+ - run: scripts/lint.bash
+
+ actionlint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check workflow files
+ uses: docker://rhysd/actionlint:1.6.27
+ with:
+ args: -color
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..5de2436
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,18 @@
+name: Release
+
+on:
+ push:
+ branches:
+ - main
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ release-please:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: google-github-actions/release-please-action@v4
+ with:
+ release-type: simple
diff --git a/.github/workflows/semantic-pr.yml b/.github/workflows/semantic-pr.yml
new file mode 100644
index 0000000..e6b3c9d
--- /dev/null
+++ b/.github/workflows/semantic-pr.yml
@@ -0,0 +1,18 @@
+name: Lint
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ - edited
+ - synchronize
+
+jobs:
+ semantic-pr:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: amannn/action-semantic-pull-request@v5.4.0
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ validateSingleCommit: true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f97357f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,54 @@
+# Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode
+# Edit at https://www.toptal.com/developers/gitignore?templates=macos,visualstudiocode
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### macOS Patch ###
+# iCloud generated files
+*.icloud
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+### VisualStudioCode Patch ###
+# Ignore all local history of files
+.history
+.ionide
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 0000000..ada5f70
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1,2 @@
+shellcheck 0.10.0
+shfmt 3.8.0
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..02bc034
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+
+MIT License
+
+Copyright (c) [year] [fullname]
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..357a716
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+
+
+# asdf-xcbeautify [![Build](https://github.com/MacPaw/asdf-xcbeautify/actions/workflows/build.yml/badge.svg)](https://github.com/MacPaw/asdf-xcbeautify/actions/workflows/build.yml) [![Lint](https://github.com/MacPaw/asdf-xcbeautify/actions/workflows/lint.yml/badge.svg)](https://github.com/MacPaw/asdf-xcbeautify/actions/workflows/lint.yml)
+
+[xcbeautify](https://github.com/cpisciotta/xcbeautify) plugin for the [asdf version manager](https://asdf-vm.com).
+
+
+
+# Contents
+
+- [Dependencies](#dependencies)
+- [Install](#install)
+- [Contributing](#contributing)
+- [License](#license)
+
+# Dependencies
+
+- `bash`, `curl`, `tar`, and [POSIX utilities](https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html).
+
+# Install
+
+Plugin:
+
+```shell
+asdf plugin add xcbeautify
+# or
+asdf plugin add xcbeautify https://github.com/MacPaw/asdf-xcbeautify.git
+```
+
+xcbeautify:
+
+```shell
+# Show all installable versions
+asdf list-all xcbeautify
+
+# Install specific version
+asdf install xcbeautify latest
+
+# Set a version globally (on your ~/.tool-versions file)
+asdf global xcbeautify latest
+
+# Now xcbeautify commands are available
+xcbeautify --help
+```
+
+Check [asdf](https://github.com/asdf-vm/asdf) readme for more instructions on how to
+install & manage versions.
+
+# Contributing
+
+Contributions of any kind welcome! See the [contributing guide](contributing.md).
+
+[Thanks goes to these contributors](https://github.com/MacPaw/asdf-xcbeautify/graphs/contributors)!
+
+# License
+
+See [LICENSE](LICENSE) © [MacPaw](https://github.com/MacPaw/)
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..0b6878f
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,23 @@
+# Security Policy
+
+## Reporting Security Issues
+If you believe you have found a security vulnerability in any MacPaw-owned repository, please report it to us through coordinated disclosure.
+
+Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.
+
+Instead, please send an email to security[@]macpaw.com.
+
+Please include as much of the information listed below as you can to help us better understand and resolve the issue:
+
+- The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting)
+- Full paths of source file(s) related to the manifestation of the issue
+- The location of the affected source code (tag/branch/commit or direct URL)
+- Any special configuration required to reproduce the issue
+- Step-by-step instructions to reproduce the issue
+- Proof-of-concept or exploit code (if possible)
+- Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+## Policy
+See MacPaw's [Vulnerability Disclosure Policy](https://macpaw.com/vulnerability-disclosure-policy)
diff --git a/bin/download b/bin/download
new file mode 100755
index 0000000..d198e41
--- /dev/null
+++ b/bin/download
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+current_script_path=${BASH_SOURCE[0]}
+plugin_dir=$(dirname "$(dirname "$current_script_path")")
+
+# shellcheck source=./lib/utils.bash
+source "${plugin_dir}/lib/utils.bash"
+
+mkdir -p "$ASDF_DOWNLOAD_PATH"
+
+# TODO: Adapt this to proper extension and adapt extracting strategy.
+release_file="$ASDF_DOWNLOAD_PATH/$TOOL_NAME-$ASDF_INSTALL_VERSION.zip"
+
+# Download tar.gz file to the download directory
+download_release "$ASDF_INSTALL_VERSION" "$release_file"
+
+# Extract contents of tar.gz file into the download directory
+# tar -xzf "$release_file" -C "$ASDF_DOWNLOAD_PATH" --strip-components=1 || fail "Could not extract $release_file"
+if [ "$(uname)" == "Darwin" ]; then
+ unzip -o "$release_file" -d "$ASDF_DOWNLOAD_PATH" >/dev/null || fail "Could not extract $release_file"
+else
+ tar -xJf "$release_file" -C "$ASDF_DOWNLOAD_PATH" --strip-components=1 || fail "Could not extract $release_file"
+ ls -la "$ASDF_DOWNLOAD_PATH"
+fi
+
+# Remove the tar.gz file since we don't need to keep it
+rm "$release_file"
diff --git a/bin/install b/bin/install
new file mode 100755
index 0000000..6ca759a
--- /dev/null
+++ b/bin/install
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+current_script_path=${BASH_SOURCE[0]}
+plugin_dir=$(dirname "$(dirname "$current_script_path")")
+
+# shellcheck source=./lib/utils.bash
+source "${plugin_dir}/lib/utils.bash"
+
+install_version "$ASDF_INSTALL_TYPE" "$ASDF_INSTALL_VERSION" "$ASDF_INSTALL_PATH"
diff --git a/bin/latest-stable b/bin/latest-stable
new file mode 100755
index 0000000..0b17b50
--- /dev/null
+++ b/bin/latest-stable
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+current_script_path=${BASH_SOURCE[0]}
+plugin_dir=$(dirname "$(dirname "$current_script_path")")
+
+# shellcheck source=./lib/utils.bash
+. "${plugin_dir}/lib/utils.bash"
+
+curl_opts=(-sI)
+
+GITHUB_API_TOKEN="${GITHUB_API_TOKEN:-${GH_API_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN}}}}"
+if [ -n "${GITHUB_API_TOKEN:-}" ]; then
+ curl_opts=("${curl_opts[@]}" -H "Authorization: token $GITHUB_API_TOKEN")
+fi
+
+# curl of REPO/releases/latest is expected to be a 302 to another URL
+# when no releases redirect_url="REPO/releases"
+# when there are releases redirect_url="REPO/releases/tag/v"
+redirect_url=$(curl "${curl_opts[@]}" "$GH_REPO/releases/latest" | sed -n -e "s|^location: *||p" | sed -n -e "s|\r||p")
+version=
+printf "redirect url: %s\n" "$redirect_url" >&2
+if [[ "$redirect_url" == "$GH_REPO/releases" ]]; then
+ version="$(list_all_versions_sorted | tail -n1 | xargs echo)"
+else
+ version="$(printf "%s\n" "$redirect_url" | sed 's|.*/tag/v\{0,1\}||')"
+fi
+
+printf "%s\n" "$version"
diff --git a/bin/list-all b/bin/list-all
new file mode 100755
index 0000000..252803f
--- /dev/null
+++ b/bin/list-all
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+current_script_path=${BASH_SOURCE[0]}
+plugin_dir=$(dirname "$(dirname "$current_script_path")")
+
+# shellcheck source=./lib/utils.bash
+source "${plugin_dir}/lib/utils.bash"
+
+list_all_versions_sorted | xargs echo
diff --git a/contributing.md b/contributing.md
new file mode 100644
index 0000000..4a4bd61
--- /dev/null
+++ b/contributing.md
@@ -0,0 +1,12 @@
+# Contributing
+
+Testing Locally:
+
+```shell
+asdf plugin test [--asdf-tool-version ] [--asdf-plugin-gitref ] [test-command*]
+
+# TODO: adapt this
+asdf plugin test xcbeautify https://github.com/MacPaw/asdf-xcbeautify.git "xcbeautify --help"
+```
+
+Tests are automatically run in GitHub Actions on push and PR.
diff --git a/lib/utils.bash b/lib/utils.bash
new file mode 100644
index 0000000..a565359
--- /dev/null
+++ b/lib/utils.bash
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+GH_REPO="https://github.com/cpisciotta/xcbeautify"
+TOOL_NAME="xcbeautify"
+TOOL_TEST="xcbeautify --help"
+
+fail() {
+ echo -e "asdf-$TOOL_NAME: $*"
+ exit 1
+}
+
+curl_opts=(-fsSL)
+
+GITHUB_API_TOKEN="${GITHUB_API_TOKEN:-${GH_API_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN}}}}"
+if [ -n "${GITHUB_API_TOKEN:-}" ]; then
+ curl_opts=("${curl_opts[@]}" -H "Authorization: token $GITHUB_API_TOKEN")
+fi
+
+list_github_tags_sorted() {
+ git -c 'versionsort.suffix=-beta' ls-remote --tags --refs --sort version:refname "$GH_REPO" |
+ grep -o 'refs/tags/.*' | cut -d/ -f3- |
+ sed 's/^v//' # NOTE: You might want to adapt this sed to remove non-version strings from tags
+}
+
+list_all_versions_sorted() {
+ # TODO: Adapt this. By default we simply list the tag names from GitHub releases.
+ # Change this function if xcbeautify has other means of determining installable versions.
+ list_github_tags_sorted
+}
+
+download_release() {
+ local version filename url
+ version="$1"
+ filename="$2"
+
+ if [ "$(uname)" == "Darwin" ]; then
+ url="$GH_REPO/releases/download/${version}/xcbeautify-${version}-universal-apple-macosx.zip"
+ else
+ url="$GH_REPO/releases/download/${version}/xcbeautify-${version}-x86_64-unknown-linux-gnu.tar.xz"
+ fi
+
+ echo "* Downloading $TOOL_NAME release $version..."
+ curl "${curl_opts[@]}" -o "$filename" -C - "$url" || fail "Could not download $url"
+}
+
+install_version() {
+ local install_type="$1"
+ local version="$2"
+ local install_path="${3%/bin}/bin"
+
+ if [ "$install_type" != "version" ]; then
+ fail "asdf-$TOOL_NAME supports release installs only"
+ fi
+
+ (
+ mkdir -p "$install_path"
+
+ if [ "$(uname)" == "Darwin" ]; then
+ cp -r "${ASDF_DOWNLOAD_PATH}/release/"* "$install_path"
+ else
+ cp -r "$ASDF_DOWNLOAD_PATH"* "$install_path"
+ fi
+
+ # TODO: Assert xcbeautify executable exists.
+ local tool_cmd
+ tool_cmd="$(echo "$TOOL_TEST" | cut -d' ' -f1)"
+ test -x "$install_path/$tool_cmd" || fail "Expected $install_path/$tool_cmd to be executable."
+
+ echo "$TOOL_NAME $version installation was successful!"
+ ) || (
+ rm -rf "$install_path"
+ fail "An error occurred while installing $TOOL_NAME $version."
+ )
+}
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..5db72dd
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended"
+ ]
+}
diff --git a/scripts/format.bash b/scripts/format.bash
new file mode 100755
index 0000000..1a216ea
--- /dev/null
+++ b/scripts/format.bash
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+shfmt --language-dialect bash --write \
+ ./**/*
diff --git a/scripts/lint.bash b/scripts/lint.bash
new file mode 100755
index 0000000..3451a05
--- /dev/null
+++ b/scripts/lint.bash
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+shellcheck --shell=bash --external-sources \
+ bin/* --source-path=template/lib/ \
+ lib/* \
+ scripts/*
+
+shfmt --language-dialect bash --diff \
+ ./**/*
diff --git a/version.txt b/version.txt
new file mode 100644
index 0000000..e69de29