Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ builds:
goos:
- darwin
- linux
- windows
goarch:
- amd64
- arm64
Expand All @@ -25,6 +26,9 @@ builds:
archives:
- format: tar.gz
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
format: zip

checksum:
name_template: "checksums.txt"
Expand Down
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@ All notable changes to skern are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.2.1] — 2026-05-03

Cross-platform install. No code changes; release pipeline + install UX only.

### Added

- **Windows binaries.** Releases now publish `skern_<version>_windows_amd64.zip`
and `skern_<version>_windows_arm64.zip` alongside the existing macOS and Linux
tarballs. Triggered by adding `windows` to the goreleaser build matrix and a
`format_overrides` rule that ships Windows as zip per convention.
- **`scripts/install.ps1`** — PowerShell installer mirroring `scripts/install.sh`.
Detects amd64/arm64, downloads the matching zip, verifies SHA-256 against the
release's `checksums.txt`, extracts to `%LOCALAPPDATA%\skern\bin`, and warns
if the install dir is not on `PATH`. Honors `SKERN_INSTALL_DIR` and
`SKERN_VERSION` environment variables, same as the Unix script.
- **`INSTALL.md`** at repo root — single canonical install guide with one
command per OS (macOS / Linux / Windows), plus version-pinning, manual
install, source build, and uninstall sections. Designed as a clean structured
doc that an LLM agent can follow end-to-end.

### Changed

- README install section now shows the three OS one-liners side-by-side and
links to `INSTALL.md` for full coverage.

[v0.2.1]: https://github.com/devrimcavusoglu/skern/compare/v0.2.0...v0.2.1

## [v0.2.0] — 2026-05-03

Dynamic skill loading release. **Contains breaking changes.**
Expand Down
76 changes: 76 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Installing skern

Pick the section that matches your operating system. Each section contains exactly one install command. After running it, verify with `skern --version`.

## macOS

```sh
curl -fsSL https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.sh | bash
```

## Linux

```sh
curl -fsSL https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.sh | bash
```

## Windows

```powershell
irm https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.ps1 | iex
```

If your PowerShell execution policy blocks the script:

```powershell
powershell -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.ps1 | iex"
```

## Verify

```sh
skern --version
```

If the command is not found, the install directory is not yet on your `PATH`. The installer prints PATH instructions for your shell at the end of its output — follow those, then open a new shell.

## Default install locations

| OS | Path |
|-----------------|---------------------------------|
| macOS / Linux | `~/.local/bin/skern` |
| Windows | `%LOCALAPPDATA%\skern\bin\skern.exe` |

Override with the `SKERN_INSTALL_DIR` environment variable.

## Pin a specific version

```sh
SKERN_VERSION=v0.2.1 curl -fsSL https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.sh | bash
```

```powershell
$env:SKERN_VERSION = 'v0.2.1'; irm https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.ps1 | iex
```

## Build from source

Requires Go 1.25+:

```sh
go install github.com/devrimcavusoglu/skern/cmd/skern@latest
```

## Manual install

Download the archive for your platform from the [releases page](https://github.com/devrimcavusoglu/skern/releases/latest), extract it, and place the `skern` (or `skern.exe`) binary on your `PATH`. Archive naming:

- `skern_<version>_darwin_<arch>.tar.gz`
- `skern_<version>_linux_<arch>.tar.gz`
- `skern_<version>_windows_<arch>.zip`

`<arch>` is `amd64` or `arm64`. Each release also publishes `checksums.txt` with SHA-256 sums.

## Uninstall

Delete the binary at the install location shown above. The installer does not modify any other files.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,26 @@ skern skill install code-review --platform claude-code

## Install

**macOS / Linux:**

```sh
curl -fsSL https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.sh | bash
```

Or with Go 1.25+:
**Windows (PowerShell):**

```powershell
irm https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.ps1 | iex
```

**From source** (Go 1.25+):

```sh
go install github.com/devrimcavusoglu/skern/cmd/skern@latest
```

Full installation guide — including version pinning, custom install paths, manual install, and uninstall — is in [INSTALL.md](./INSTALL.md).

## Documentation

Full documentation is available at **[skern.dev](https://skern.dev)**:
Expand Down
172 changes: 172 additions & 0 deletions scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Skern installer - Windows / PowerShell counterpart to scripts/install.sh
# Usage:
# irm https://raw.githubusercontent.com/devrimcavusoglu/skern/main/scripts/install.ps1 | iex
#
# Environment variables:
# SKERN_INSTALL_DIR - override install directory (default: %LOCALAPPDATA%\skern\bin)
# SKERN_VERSION - install a specific version (default: latest)

$ErrorActionPreference = 'Stop'

$Repo = 'devrimcavusoglu/skern'
$Binary = 'skern'
$DefaultInstallDir = Join-Path $env:LOCALAPPDATA 'skern\bin'

# PowerShell 5.1 default protocol may be TLS 1.0/1.1; GitHub requires 1.2+.
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

function Write-Info($msg) {
Write-Host "[skern] $msg"
}

function Stop-WithError($msg) {
Write-Host "[skern] ERROR: $msg" -ForegroundColor Red
exit 1
}

function Get-Arch {
$procArch = $env:PROCESSOR_ARCHITECTURE
$procArchW6432 = $env:PROCESSOR_ARCHITEW6432
if ($procArch -eq 'ARM64' -or $procArchW6432 -eq 'ARM64') {
return 'arm64'
}
if ([System.Environment]::Is64BitOperatingSystem) {
return 'amd64'
}
Stop-WithError "Unsupported architecture: $procArch. Skern supports amd64 and arm64."
}

function Resolve-Version {
if ($env:SKERN_VERSION) {
Write-Info "Using specified version: $($env:SKERN_VERSION)"
return $env:SKERN_VERSION
}
Write-Info 'Fetching latest release...'
try {
$release = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" -UseBasicParsing
} catch {
Stop-WithError "Could not fetch latest release: $_"
}
if (-not $release.tag_name) {
Stop-WithError "Could not determine latest version. Set SKERN_VERSION manually or use 'go install'."
}
Write-Info "Latest version: $($release.tag_name)"
return $release.tag_name
}

function Get-AssetWithChecksum($version, $arch, $tmpDir) {
$versionNoV = $version -replace '^v',''
$assetName = "skern_${versionNoV}_windows_${arch}.zip"
$downloadUrl = "https://github.com/$Repo/releases/download/$version/$assetName"
$checksumsUrl = "https://github.com/$Repo/releases/download/$version/checksums.txt"

$zipPath = Join-Path $tmpDir $assetName
$checksumsPath = Join-Path $tmpDir 'checksums.txt'

Write-Info "Downloading $downloadUrl ..."
try {
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing
} catch {
Stop-WithError "Download failed: $_"
}

try {
Invoke-WebRequest -Uri $checksumsUrl -OutFile $checksumsPath -UseBasicParsing
} catch {
Write-Info 'Warning: checksums file not available, skipping verification.'
$checksumsPath = $null
}

if ($checksumsPath -and (Test-Path $checksumsPath)) {
$expectedLine = Get-Content $checksumsPath | Where-Object { $_ -match [regex]::Escape($assetName) } | Select-Object -First 1
if ($expectedLine) {
$expectedSum = ($expectedLine -split '\s+')[0]
$actualSum = (Get-FileHash -Path $zipPath -Algorithm SHA256).Hash.ToLower()
if ($expectedSum.ToLower() -ne $actualSum) {
Stop-WithError "Checksum mismatch! Expected $expectedSum, got $actualSum. Aborting."
}
Write-Info 'Checksum verified.'
} else {
Write-Info "Warning: could not find checksum for $assetName, skipping verification."
}
}

return $zipPath
}

function Install-Binary($zipPath, $tmpDir) {
$installDir = if ($env:SKERN_INSTALL_DIR) { $env:SKERN_INSTALL_DIR } else { $DefaultInstallDir }
if (-not (Test-Path $installDir)) {
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
}

$extractDir = Join-Path $tmpDir 'extracted'
Expand-Archive -Path $zipPath -DestinationPath $extractDir -Force

$exeName = "$Binary.exe"
$sourceExe = Get-ChildItem -Path $extractDir -Filter $exeName -Recurse | Select-Object -First 1
if (-not $sourceExe) {
Stop-WithError "Binary $exeName not found after extraction. Archive may be corrupt."
}

$targetPath = Join-Path $installDir $exeName
Move-Item -Path $sourceExe.FullName -Destination $targetPath -Force
Write-Info "Installed $exeName to $targetPath"

# Check if installDir is on PATH (user or system).
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
$combinedPath = "$userPath;$machinePath"
$onPath = $combinedPath.Split(';') | Where-Object { $_.TrimEnd('\') -ieq $installDir.TrimEnd('\') }

if (-not $onPath) {
Write-Info ''
Write-Info "WARNING: $installDir is not on your PATH."
Write-Info 'Add it persistently with:'
Write-Info ''
Write-Info " [Environment]::SetEnvironmentVariable('Path', `"`$env:Path;$installDir`", 'User')"
Write-Info ''
Write-Info 'Then open a new shell. Or, for the current shell only:'
Write-Info ''
Write-Info " `$env:Path += ';$installDir'"
Write-Info ''
}

return $targetPath
}

function Main {
Write-Info 'Skern installer'
Write-Info ''

$arch = Get-Arch
Write-Info "Detected platform: windows_$arch"

$version = Resolve-Version

$tmpDir = Join-Path $env:TEMP "skern-install-$([System.Guid]::NewGuid().ToString('N'))"
New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null

try {
$zipPath = Get-AssetWithChecksum -version $version -arch $arch -tmpDir $tmpDir
$targetPath = Install-Binary -zipPath $zipPath -tmpDir $tmpDir

Write-Info ''
try {
$output = & $targetPath version 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Info "Success! Installed $output"
} else {
Write-Info 'Installation complete. You may need to open a new shell to use skern.'
}
} catch {
Write-Info 'Installation complete. You may need to open a new shell to use skern.'
}
} finally {
if (Test-Path $tmpDir) {
Remove-Item -Path $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
}

Main
Loading