Skip to content

Commit 3d880eb

Browse files
deveweberclaude
andauthored
Windows installer (PowerShell + cmd shim) and CI smoke test (#1)
* Add Windows installer (PowerShell + cmd shim) and CI smoke test install-claude46.ps1 installs @anthropic-ai/claude-code@2.1.110 via npm into an isolated %LOCALAPPDATA%\claude46 prefix, writes a claude46.cmd wrapper setting DISABLE_AUTOUPDATER, DISABLE_UPDATES, and ANTHROPIC_MODEL, and adds the bin dir to user PATH. install-claude46.cmd is a cmd-entry shim that hands off to the PowerShell installer. Retrofit the bash installer with DISABLE_UPDATES=1 for parity, document the Windows path in the README (WSL, native installer, ad-hoc npx), and add a windows-2025 CI smoke test that exercises both wrappers end-to-end. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Scope CI to main pushes/PRs with installer-only paths filter After landing, the smoke test should fire on direct pushes to main and on PRs targeting main that touch installer files or the workflow itself, not on every push to a feature branch. Manual dispatch remains. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Dimitar Stoyanov <dimitar.h.stoyanov@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6aed572 commit 3d880eb

5 files changed

Lines changed: 264 additions & 6 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: windows-installer-test
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: [main]
7+
paths:
8+
- 'install-claude46.sh'
9+
- 'install-claude46.ps1'
10+
- 'install-claude46.cmd'
11+
- '.github/workflows/windows-installer.yml'
12+
pull_request:
13+
branches: [main]
14+
paths:
15+
- 'install-claude46.sh'
16+
- 'install-claude46.ps1'
17+
- 'install-claude46.cmd'
18+
- '.github/workflows/windows-installer.yml'
19+
20+
jobs:
21+
smoke:
22+
runs-on: windows-2025
23+
defaults:
24+
run:
25+
shell: pwsh
26+
steps:
27+
- uses: actions/checkout@v6
28+
29+
- name: Show preinstalled tooling
30+
run: |
31+
node --version
32+
npm --version
33+
git --version
34+
$PSVersionTable.PSVersion
35+
36+
- name: Run install-claude46.ps1
37+
run: |
38+
npm config set fetch-retries 3
39+
./install-claude46.ps1
40+
41+
- name: Verify wrapper file exists and runs via direct path
42+
run: |
43+
$bin = Join-Path $env:LOCALAPPDATA 'claude46\bin'
44+
$exe = Join-Path $bin 'claude46.cmd'
45+
if (-not (Test-Path $exe)) { throw "Wrapper missing: $exe" }
46+
& $exe --version
47+
if ($LASTEXITCODE -ne 0) { throw "claude46.cmd --version exit $LASTEXITCODE" }
48+
49+
- name: Inspect generated wrapper content
50+
run: |
51+
$exe = Join-Path $env:LOCALAPPDATA 'claude46\bin\claude46.cmd'
52+
Write-Host "--- $exe ---"
53+
Get-Content $exe
54+
55+
- name: Assert wrapper sets expected env vars
56+
run: |
57+
$exe = Join-Path $env:LOCALAPPDATA 'claude46\bin\claude46.cmd'
58+
$content = Get-Content $exe -Raw
59+
$needles = @(
60+
'set "DISABLE_AUTOUPDATER=1"',
61+
'set "DISABLE_UPDATES=1"',
62+
'set "ANTHROPIC_MODEL=claude-opus-4-6[1m]"'
63+
)
64+
foreach ($needle in $needles) {
65+
$pattern = [regex]::Escape($needle)
66+
if ($content -notmatch $pattern) {
67+
throw "Wrapper missing expected line: $needle"
68+
}
69+
}
70+
71+
- name: Verify HKCU user PATH was updated
72+
run: |
73+
$bin = Join-Path $env:LOCALAPPDATA 'claude46\bin'
74+
$userPath = [Environment]::GetEnvironmentVariable('Path','User')
75+
if (-not $userPath) { throw "User PATH is empty/null after install" }
76+
$found = $false
77+
foreach ($entry in $userPath.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)) {
78+
$expanded = [Environment]::ExpandEnvironmentVariables($entry).TrimEnd('\')
79+
if ([string]::Equals($expanded, $bin.TrimEnd('\'), [StringComparison]::OrdinalIgnoreCase)) {
80+
$found = $true
81+
break
82+
}
83+
}
84+
if (-not $found) { throw "BinDir not on user PATH after install: expected $bin" }
85+
86+
- name: Expose claude46 on PATH for subsequent steps
87+
run: |
88+
$bin = Join-Path $env:LOCALAPPDATA 'claude46\bin'
89+
$bin | Out-File -Append -FilePath $env:GITHUB_PATH
90+
91+
- name: Resolve claude46 via PATHEXT (no .cmd suffix)
92+
run: claude46 --version
93+
94+
- name: Test claude46.cmd from cmd.exe
95+
shell: cmd
96+
run: |
97+
"%LOCALAPPDATA%\claude46\bin\claude46.cmd" --version
98+
99+
- name: Re-run installer (idempotency)
100+
run: ./install-claude46.ps1
101+
102+
- name: Test install-claude46.cmd shim entrypoint
103+
shell: cmd
104+
run: install-claude46.cmd

README.md

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ This project is not affiliated with Anthropic. Claude and Claude Code are Anthro
1010

1111
It is intended for users who want to keep running Claude Opus 4.6 after Opus 4.7 became the default, while leaving the regular `claude` command free to auto-update.
1212

13-
The script installs:
13+
On macOS/Linux, the script installs:
1414

1515
- Claude Code `2.1.110`
1616
- Launcher command: `claude46`
1717
- Default model: `claude-opus-4-6[1m]`
1818
- Binary path: `~/.local/share/claude46/claude-2.1.110`
1919
- Launcher path: `~/.local/bin/claude46`
2020

21-
The launcher sets `DISABLE_AUTOUPDATER=1`, so `claude46` stays pinned. Your normal `claude` command is not modified.
21+
(Native Windows install layout is described in the [Windows](#windows) section.)
22+
23+
The launcher sets `DISABLE_AUTOUPDATER=1` and `DISABLE_UPDATES=1`, so `claude46` stays pinned (background auto-updates blocked, and an explicit `claude46 update` is a no-op). Your normal `claude` command is not modified.
2224

2325
## Install claude46 to Keep Using Claude Opus 4.6
2426

@@ -104,7 +106,9 @@ This replaces the pinned binary and launcher at the same paths.
104106

105107
## Uninstall
106108

107-
Remove the pinned binary and launcher:
109+
Remove the pinned binary and launcher.
110+
111+
macOS / Linux / WSL:
108112

109113
```bash
110114
rm -f "$HOME/.local/bin/claude46"
@@ -117,13 +121,82 @@ Optionally remove this line from `~/.zshrc` if you no longer need it:
117121
export PATH="$HOME/.local/bin:$PATH"
118122
```
119123

124+
Native Windows:
125+
126+
```powershell
127+
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\claude46"
128+
```
129+
130+
Optionally remove `%LOCALAPPDATA%\claude46\bin` from your user PATH via System Properties → Environment Variables.
131+
120132
## Requirements
121133

122-
- macOS or Linux
134+
- macOS or Linux (including WSL)
123135
- `curl`
124136
- `bash`
125137
- `~/.local/bin` on your `PATH`
126138

139+
For native Windows, see [Windows](#windows) below.
140+
141+
## Windows
142+
143+
`install-claude46.sh` does not run on native Windows. Use one of these paths:
144+
145+
### Inside WSL (recommended)
146+
147+
Open a WSL shell and follow the bash install steps in [Install](#install-claude46-to-keep-using-claude-opus-46) / [Local Install](#local-install) above. The bash script identifies WSL as Linux, and `claude46` will be available from your WSL terminals.
148+
149+
### Native Windows: `install-claude46.ps1`
150+
151+
The PowerShell installer pins `@anthropic-ai/claude-code@2.1.110` via npm into an isolated prefix at `%LOCALAPPDATA%\claude46\`, writes a `claude46.cmd` wrapper that sets `DISABLE_AUTOUPDATER=1`, `DISABLE_UPDATES=1`, and `ANTHROPIC_MODEL=claude-opus-4-6[1m]`, and adds `%LOCALAPPDATA%\claude46\bin` to your user PATH. No admin rights needed. Your global `claude` (if any) is untouched.
152+
153+
Prerequisites:
154+
155+
- Node.js 18+ and npm
156+
- Git for Windows (Claude Code requires Git Bash on native Windows; set `CLAUDE_CODE_GIT_BASH_PATH` if Git is installed in a nonstandard location)
157+
158+
Quick install from PowerShell:
159+
160+
```powershell
161+
irm https://raw.githubusercontent.com/sparklingneuronics/claude-code-helpers/v0.2.0/install-claude46.ps1 | iex
162+
```
163+
164+
Or from `cmd.exe`:
165+
166+
```cmd
167+
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/sparklingneuronics/claude-code-helpers/v0.2.0/install-claude46.ps1 | iex"
168+
```
169+
170+
To run the latest from `main` instead, replace `v0.2.0` with `main` in the URL.
171+
172+
Local install from a cloned repo:
173+
174+
```cmd
175+
install-claude46.cmd
176+
```
177+
178+
Open a new terminal after install, then run `claude46`.
179+
180+
### Without installing (ad-hoc via npx)
181+
182+
For a one-off pinned Opus 4.6 session without installing anything globally, from PowerShell:
183+
184+
```powershell
185+
$env:DISABLE_AUTOUPDATER="1"
186+
$env:DISABLE_UPDATES="1"
187+
$env:ANTHROPIC_MODEL="claude-opus-4-6[1m]"
188+
npx --yes --package=@anthropic-ai/claude-code@2.1.110 claude
189+
```
190+
191+
From `cmd.exe`:
192+
193+
```cmd
194+
set "DISABLE_AUTOUPDATER=1"
195+
set "DISABLE_UPDATES=1"
196+
set "ANTHROPIC_MODEL=claude-opus-4-6[1m]"
197+
npx --yes --package=@anthropic-ai/claude-code@2.1.110 claude
198+
```
199+
127200
## FAQ
128201

129202
### How do I keep using Claude Opus 4.6 after Claude Code updated to Opus 4.7?
@@ -140,11 +213,11 @@ The launcher sets the default model ID to `claude-opus-4-6[1m]`, which requests
140213

141214
### Will this break my normal `claude` install?
142215

143-
No. `claude46` does not replace your normal `claude` binary — they live at different paths, `claude` keeps updating, and `claude46` stays pinned. They do share the same `~/.claude/` auth and config, so logging in for one logs in for both.
216+
No. `claude46` does not replace your normal `claude` binary — they live at different paths, `claude` keeps updating, and `claude46` stays pinned. They do share Claude Code's per-user auth/config directory (`~/.claude/` on macOS/Linux/WSL, `%USERPROFILE%\.claude\` on native Windows), so logging in for one logs in for both.
144217

145218
### How do I switch back to the latest Claude Code?
146219

147-
Just run `claude` as usual, or uninstall `claude46` with the two `rm` commands in the [Uninstall](#uninstall) section.
220+
Just run `claude` as usual, or follow the commands in the [Uninstall](#uninstall) section for your platform.
148221

149222
## Maintenance
150223

install-claude46.cmd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@echo off
2+
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0install-claude46.ps1" %*
3+
exit /b %ERRORLEVEL%

install-claude46.ps1

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#Requires -Version 5.1
2+
$ErrorActionPreference = 'Stop'
3+
4+
$Version = '2.1.110'
5+
$Model = 'claude-opus-4-6[1m]'
6+
$Root = Join-Path $env:LOCALAPPDATA 'claude46'
7+
$BinDir = Join-Path $Root 'bin'
8+
9+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
10+
Write-Error 'Node.js is required. Install from https://nodejs.org/ (18+).'
11+
exit 1
12+
}
13+
$nodeVersion = (& node --version).TrimStart('v')
14+
$nodeMajor = [int]($nodeVersion.Split('.')[0])
15+
if ($nodeMajor -lt 18) {
16+
Write-Error "Node.js 18+ required (found v$nodeVersion). Update from https://nodejs.org/."
17+
exit 1
18+
}
19+
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
20+
Write-Error 'npm is required and was not found on PATH.'
21+
exit 1
22+
}
23+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
24+
Write-Warning 'git not found on PATH. Claude Code needs Git for Windows on native Windows. Install from https://gitforwindows.org/ (or set CLAUDE_CODE_GIT_BASH_PATH if Git is installed in a nonstandard location).'
25+
}
26+
27+
New-Item -ItemType Directory -Force -Path $Root, $BinDir | Out-Null
28+
29+
$pkgJson = Join-Path $Root 'package.json'
30+
if (-not (Test-Path $pkgJson)) {
31+
'{"name":"claude46","private":true}' | Set-Content -Path $pkgJson -Encoding ASCII
32+
}
33+
34+
Write-Host "Installing @anthropic-ai/claude-code@$Version into $Root ..."
35+
& npm install --prefix $Root "@anthropic-ai/claude-code@$Version"
36+
if ($LASTEXITCODE -ne 0) {
37+
Write-Error "npm install failed (exit $LASTEXITCODE)."
38+
exit 1
39+
}
40+
41+
$wrapperPath = Join-Path $BinDir 'claude46.cmd'
42+
$wrapperLines = @(
43+
'@echo off',
44+
'set "DISABLE_AUTOUPDATER=1"',
45+
'set "DISABLE_UPDATES=1"',
46+
"set `"ANTHROPIC_MODEL=$Model`"",
47+
'call "%~dp0..\node_modules\.bin\claude.cmd" %*'
48+
)
49+
Set-Content -Path $wrapperPath -Value $wrapperLines -Encoding ASCII
50+
51+
$userPath = [Environment]::GetEnvironmentVariable('PATH', 'User')
52+
if ($null -eq $userPath) { $userPath = '' }
53+
$normalizedTarget = $BinDir.TrimEnd('\')
54+
$alreadyOnPath = $false
55+
foreach ($entry in $userPath.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)) {
56+
$expanded = [Environment]::ExpandEnvironmentVariables($entry).Trim('"').TrimEnd('\')
57+
if ([string]::Equals($expanded, $normalizedTarget, [StringComparison]::OrdinalIgnoreCase)) {
58+
$alreadyOnPath = $true
59+
break
60+
}
61+
}
62+
if (-not $alreadyOnPath) {
63+
$newPath = if ($userPath) { "$userPath;$BinDir" } else { $BinDir }
64+
[Environment]::SetEnvironmentVariable('PATH', $newPath, 'User')
65+
Write-Host "Added $BinDir to user PATH."
66+
} else {
67+
Write-Host "$BinDir already on user PATH."
68+
}
69+
70+
Write-Host 'Installed claude46:'
71+
& $wrapperPath --version
72+
if ($LASTEXITCODE -ne 0) {
73+
Write-Error "claude46 --version failed (exit $LASTEXITCODE). Install may be incomplete."
74+
exit 1
75+
}
76+
Write-Host ''
77+
Write-Host 'Start it with: claude46 (open a new terminal first so the PATH change takes effect)'

install-claude46.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ chmod +x "$INSTALL_DIR/claude-$VERSION"
4444
cat > "$BIN_DIR/claude46" <<EOF
4545
#!/usr/bin/env bash
4646
export DISABLE_AUTOUPDATER=1
47+
export DISABLE_UPDATES=1
4748
export ANTHROPIC_MODEL='$MODEL'
4849
exec "$INSTALL_DIR/claude-$VERSION" "\$@"
4950
EOF

0 commit comments

Comments
 (0)