Skip to content

Commit b26afa2

Browse files
tizz98claude
andauthored
feat: native Windows launchers (PowerShell/cmd) + install.ps1 (#18)
* docs: add design spec for native Windows meta launchers Route PowerShell/cmd through a policy-bypassing meta.cmd -> .meta/shim.ps1; keep no root .ps1 so `./meta` is execution-policy-proof. Adds install.ps1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: add implementation plan for native Windows meta launchers Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(scaffold): generate Windows meta.cmd + .meta/shim.ps1 launchers Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(scaffold): fail closed on malformed checksum; ASCII-only Windows shims Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: align plan checksum block with fail-closed fix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(install): add native Windows install.ps1 bootstrapper Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: align plan install.ps1 checksum with fail-closed fix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: document native PowerShell/cmd Windows launcher path Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: dogfood Windows launchers into this repo Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: pin this repo to v0.4.0 so Windows launchers resolve a binary Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: pin bash shim + shim.sh + .meta/version to LF for Windows CRLF safety A CRLF checkout on Windows (core.autocrlf) breaks the ./meta bash shim under Git Bash (CRLF shebang; CR in the version string corrupts the release URL), and a CRLF src/assets/shim.sh gets embedded via include_str! and shipped as a broken bash shim to every managed repo. Pin the bash-path files to LF; .cmd stays CRLF. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: stop tracking superpowers planning docs The spec/plan under docs/superpowers/ are local planning artifacts, not part of the deliverable. Untrack (keep on disk) and gitignore the directory. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 84d2e3a commit b26afa2

12 files changed

Lines changed: 320 additions & 5 deletions

File tree

.gitattributes

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Windows batch must be CRLF for maximum cmd.exe compatibility; the embedded
2+
# copy ships verbatim to every managed repo. PowerShell tolerates either — pin LF.
3+
*.cmd text eol=crlf
4+
*.ps1 text eol=lf
5+
# Bash scripts and the pinned-version file MUST stay LF: a CRLF shebang or a
6+
# CRLF `.meta/version` breaks the `./meta` bash shim under Git Bash on Windows,
7+
# and a CRLF src/assets/shim.sh would be embedded (include_str!) into the binary
8+
# and shipped as a broken shim to every managed repo.
9+
*.sh text eol=lf
10+
/meta text eol=lf
11+
.meta/version text eol=lf

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ target
2020
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
2121
#.idea/
2222

23+
# superpowers planning docs (spec/plan) — kept local, not committed
24+
/docs/superpowers/
25+
2326
# ai-meta
2427
/.meta/state/
2528
/.meta/bin/

.meta/meta.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
[meta]
66
schema_version = 1
7-
framework_version = "0.1.0"
7+
framework_version = "0.4.0"
88
profile = "rust"
99

1010
[project]

.meta/shim.ps1

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#Requires -Version 5.1
2+
# .meta/shim.ps1 - PowerShell launcher for the ai-meta binary. GENERATED by
3+
# `meta init/upgrade`. Do not edit; the pinned version lives in .meta/version.
4+
# Reached via the repo-root meta.cmd dispatcher. See
5+
# https://github.com/tizz98/ai-meta
6+
$ErrorActionPreference = 'Stop'
7+
8+
# The pinned version sits next to this script (.meta/version).
9+
$verFile = Join-Path $PSScriptRoot 'version'
10+
$ver = ''
11+
if (Test-Path -LiteralPath $verFile) {
12+
$raw = Get-Content -Raw -LiteralPath $verFile
13+
if ($raw) { $ver = $raw.Trim() }
14+
}
15+
if (-not $ver) {
16+
[Console]::Error.WriteLine("meta: missing .meta/version (run 'meta init')")
17+
exit 1
18+
}
19+
20+
# Map the host arch to a release target. Only x86_64 Windows is published today.
21+
switch ($env:PROCESSOR_ARCHITECTURE) {
22+
'AMD64' { $tgt = 'x86_64-pc-windows-msvc'; $ext = '.exe' }
23+
'x86' { $tgt = 'x86_64-pc-windows-msvc'; $ext = '.exe' }
24+
default {
25+
[Console]::Error.WriteLine("meta: unsupported Windows arch $($env:PROCESSOR_ARCHITECTURE) (no published build)")
26+
exit 1
27+
}
28+
}
29+
30+
# Share the download cache with the bash shim ($HOME/.cache/ai-meta/<ver>).
31+
$cacheRoot = if ($env:AI_META_CACHE) { $env:AI_META_CACHE } else { Join-Path $HOME '.cache\ai-meta' }
32+
$cache = Join-Path $cacheRoot $ver
33+
$asset = "ai-meta-$tgt$ext"
34+
$bin = Join-Path $cache $asset
35+
36+
if (-not (Test-Path -LiteralPath $bin)) {
37+
New-Item -ItemType Directory -Force -Path $cache | Out-Null
38+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
39+
$ProgressPreference = 'SilentlyContinue'
40+
$base = "https://github.com/tizz98/ai-meta/releases/download/v$ver"
41+
$tmp = "$bin.tmp"
42+
try {
43+
Invoke-WebRequest -UseBasicParsing -Uri "$base/$asset" -OutFile $tmp
44+
} catch {
45+
[Console]::Error.WriteLine("meta: failed to download ai-meta v$ver for $tgt from $base")
46+
exit 1
47+
}
48+
# Verify the checksum when the release publishes one (it always should).
49+
$sumFile = "$bin.sha256"
50+
$haveSum = $false
51+
try {
52+
Invoke-WebRequest -UseBasicParsing -Uri "$base/$asset.sha256" -OutFile $sumFile
53+
$haveSum = $true
54+
} catch { }
55+
if ($haveSum) {
56+
$rawSum = Get-Content -Raw -LiteralPath $sumFile
57+
$want = if ($rawSum) { (($rawSum.Trim()) -split '\s+')[0].ToLower() } else { '' }
58+
$have = (Get-FileHash -Algorithm SHA256 -LiteralPath $tmp).Hash.ToLower()
59+
if ($want -ne $have) {
60+
Remove-Item -Force -LiteralPath $tmp
61+
[Console]::Error.WriteLine("meta: checksum mismatch for ai-meta v$ver ($tgt)")
62+
exit 1
63+
}
64+
}
65+
Move-Item -Force -LiteralPath $tmp -Destination $bin
66+
}
67+
68+
# Match `exec "$bin" "$@"` - inherit cwd; do not let a nonzero native exit throw.
69+
$ErrorActionPreference = 'Continue'
70+
& $bin @args
71+
exit $LASTEXITCODE

.meta/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0
1+
0.4.0

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ exclude = [
2424
"docs/",
2525
"META.md",
2626
"install.sh",
27+
"install.ps1",
2728
"meta",
29+
"meta.cmd",
2830
".gitignore",
2931
]
3032

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,19 @@ cargo install ai-meta
2828
```
2929

3030
Prebuilt binaries ship for Linux (x86_64/aarch64, musl), macOS (Intel/Apple
31-
Silicon), and Windows (x86_64). On Windows, run `install.sh` and the `./meta`
32-
shim from Git Bash / MSYS2 / Cygwin; both resolve the `x86_64-pc-windows-msvc`
33-
build automatically.
31+
Silicon), and Windows (x86_64).
32+
33+
On Windows there are two paths. **Native PowerShell / cmd.exe** — install with
34+
35+
```
36+
irm https://raw.githubusercontent.com/tizz98/ai-meta/main/install.ps1 | iex
37+
```
38+
39+
then use `./meta <cmd>` in PowerShell or `.\meta <cmd>` in cmd.exe. The generated
40+
`meta.cmd` dispatcher relaunches PowerShell with an execution-policy bypass, so
41+
`./meta` works under any policy — no setup. **Git Bash / MSYS2 / Cygwin** — run
42+
`install.sh` and use the `./meta` bash shim as on Unix. All paths resolve the
43+
`x86_64-pc-windows-msvc` build automatically and share one download cache.
3444

3545
## What it does
3646

install.ps1

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#Requires -Version 5.1
2+
<#
3+
install.ps1 - one-time installer for the ai-meta `meta` CLI (native Windows).
4+
5+
irm https://raw.githubusercontent.com/tizz98/ai-meta/main/install.ps1 | iex
6+
7+
Downloads the `meta` binary from GitHub releases, verifies its checksum, and
8+
installs it. After installing, run `meta init` in a repo.
9+
10+
Tunables (environment variables):
11+
AI_META_VERSION pin a version (e.g. 0.4.0); default: latest release.
12+
AI_META_BIN_DIR install directory; default: %LOCALAPPDATA%\ai-meta\bin.
13+
AI_META_REPO owner/repo to fetch from; default: tizz98/ai-meta.
14+
#>
15+
$ErrorActionPreference = 'Stop'
16+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
17+
$ProgressPreference = 'SilentlyContinue'
18+
19+
$repo = if ($env:AI_META_REPO) { $env:AI_META_REPO } else { 'tizz98/ai-meta' }
20+
$binDir = if ($env:AI_META_BIN_DIR) { $env:AI_META_BIN_DIR } else { Join-Path $env:LOCALAPPDATA 'ai-meta\bin' }
21+
$version = $env:AI_META_VERSION
22+
23+
$arch = $env:PROCESSOR_ARCHITECTURE
24+
if ($arch -ne 'AMD64' -and $arch -ne 'x86') {
25+
throw "install: unsupported Windows arch $arch (no published build)"
26+
}
27+
$tgt = 'x86_64-pc-windows-msvc'
28+
$ext = '.exe'
29+
30+
if ($version) {
31+
$version = $version.TrimStart('v')
32+
$base = "https://github.com/$repo/releases/download/v$version"
33+
$label = "v$version"
34+
} else {
35+
$base = "https://github.com/$repo/releases/latest/download"
36+
$label = 'latest'
37+
}
38+
$asset = "ai-meta-$tgt$ext"
39+
$url = "$base/$asset"
40+
41+
$tmp = Join-Path ([IO.Path]::GetTempPath()) ("ai-meta-install-" + [Guid]::NewGuid().ToString('N'))
42+
New-Item -ItemType Directory -Force -Path $tmp | Out-Null
43+
try {
44+
Write-Host "downloading meta ($label) for $tgt..."
45+
$dl = Join-Path $tmp $asset
46+
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $dl
47+
48+
# Verify the checksum when the release publishes one (it always should).
49+
$sumFile = Join-Path $tmp "$asset.sha256"
50+
$haveSum = $false
51+
try {
52+
Invoke-WebRequest -UseBasicParsing -Uri "$url.sha256" -OutFile $sumFile
53+
$haveSum = $true
54+
} catch {
55+
Write-Warning "no checksum published for $asset; skipping verification"
56+
}
57+
if ($haveSum) {
58+
$rawSum = Get-Content -Raw -LiteralPath $sumFile
59+
$want = if ($rawSum) { (($rawSum.Trim()) -split '\s+')[0].ToLower() } else { '' }
60+
$have = (Get-FileHash -Algorithm SHA256 -LiteralPath $dl).Hash.ToLower()
61+
if ($want -ne $have) {
62+
throw "install: checksum mismatch for $asset (expected $want, got $have)"
63+
}
64+
}
65+
66+
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
67+
$dest = Join-Path $binDir "meta$ext"
68+
Move-Item -Force -LiteralPath $dl -Destination $dest
69+
Write-Host "installed meta -> $dest"
70+
} finally {
71+
Remove-Item -Recurse -Force -LiteralPath $tmp -ErrorAction SilentlyContinue
72+
}
73+
74+
# Nudge (do not auto-edit PATH) if the install dir isn't on PATH.
75+
if (($env:PATH -split ';') -notcontains $binDir) {
76+
Write-Host ""
77+
Write-Host "note: $binDir is not on your PATH. Add it (User scope), then restart your shell:"
78+
Write-Host " [Environment]::SetEnvironmentVariable('Path', (`"$binDir;`" + [Environment]::GetEnvironmentVariable('Path','User')), 'User')"
79+
}
80+
Write-Host ""
81+
Write-Host "done. Run 'meta init' in a repo to get started."

meta.cmd

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@echo off
2+
setlocal
3+
rem meta.cmd - Windows dispatcher for the ai-meta launcher. GENERATED by
4+
rem `meta init/upgrade`. Routes PowerShell (./meta) and cmd.exe (.\meta) through
5+
rem PowerShell with -ExecutionPolicy Bypass into .meta\shim.ps1, so `./meta` works
6+
rem under any execution policy. Do not edit. https://github.com/tizz98/ai-meta
7+
set "PS=powershell"
8+
where /q pwsh && set "PS=pwsh"
9+
"%PS%" -NoProfile -ExecutionPolicy Bypass -File "%~dp0.meta\shim.ps1" %*
10+
exit /b %errorlevel%

src/assets/shim.cmd

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@echo off
2+
setlocal
3+
rem meta.cmd - Windows dispatcher for the ai-meta launcher. GENERATED by
4+
rem `meta init/upgrade`. Routes PowerShell (./meta) and cmd.exe (.\meta) through
5+
rem PowerShell with -ExecutionPolicy Bypass into .meta\shim.ps1, so `./meta` works
6+
rem under any execution policy. Do not edit. https://github.com/tizz98/ai-meta
7+
set "PS=powershell"
8+
where /q pwsh && set "PS=pwsh"
9+
"%PS%" -NoProfile -ExecutionPolicy Bypass -File "%~dp0.meta\shim.ps1" %*
10+
exit /b %errorlevel%

0 commit comments

Comments
 (0)