|
@echo off |
|
REM ===================================================================== |
|
REM InstallClaude.cmd - run ONCE, on a machine you trust. |
|
REM Downloads + verifies claude.exe, writes an INERT, self-elevating |
|
REM Claude.cmd plus config.txt, a field-IT CLAUDE.md, and a safe-fast |
|
REM settings.json, then opens Claude Code for a one-time login. |
|
REM Everything below the "#PS-PAYLOAD#" line is PowerShell, run by the |
|
REM line just below. cmd never runs it. It is not obfuscated - read it. |
|
REM ===================================================================== |
|
setlocal |
|
set "ROOT=%~dp0" |
|
set "BUCKET=https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases" |
|
md "%ROOT%bin" 2>nul & md "%ROOT%data" 2>nul & md "%ROOT%tmp" 2>nul & md "%ROOT%work" 2>nul |
|
|
|
powershell -NoProfile -ExecutionPolicy Bypass -Command "$t=[IO.File]::ReadAllText('%~f0'); iex $t.Substring($t.LastIndexOf([char]10+'#PS-PAYLOAD#'))" || (echo. & echo Install failed - nothing was kept. Run on a trusted machine with internet. & echo. & pause & exit /b 1) |
|
|
|
echo. |
|
echo Setup complete. Opening Claude Code so you can log in... |
|
echo After you log in, type /exit , then eject the stick. |
|
echo. |
|
set "CLAUDE_CONFIG_DIR=%ROOT%data" |
|
set "DISABLE_AUTOUPDATER=1" |
|
cd /d "%ROOT%work" |
|
"%ROOT%bin\claude.exe" |
|
exit /b 0 |
|
|
|
#PS-PAYLOAD# |
|
$ErrorActionPreference = 'Stop' |
|
$root = $env:ROOT |
|
$bucket = $env:BUCKET |
|
$plat = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'win32-arm64' } else { 'win32-x64' } |
|
$exe = Join-Path $root 'bin\claude.exe' |
|
|
|
if (-not (Test-Path $exe)) { |
|
Write-Host " Downloading Claude Code ($plat)..." |
|
$ver = (Invoke-WebRequest "$bucket/latest" -UseBasicParsing).Content.Trim() |
|
$want = ((Invoke-WebRequest "$bucket/$ver/manifest.json" -UseBasicParsing).Content | ConvertFrom-Json).platforms.$plat.checksum |
|
Invoke-WebRequest "$bucket/$ver/$plat/claude.exe" -OutFile $exe -UseBasicParsing |
|
if ((Get-FileHash $exe -Algorithm SHA256).Hash.ToLower() -ne $want.ToLower()) { Remove-Item $exe; throw "SHA256 mismatch - aborted." } |
|
$sig = Get-AuthenticodeSignature $exe |
|
if ($sig.Status -ne 'Valid' -or $sig.SignerCertificate.Subject -notlike '*Anthropic*') { Remove-Item $exe; throw "Not signed by Anthropic - aborted." } |
|
Write-Host " Verified claude.exe v$ver - $($sig.SignerCertificate.Subject)" |
|
} else { |
|
Write-Host " claude.exe already present - skipping download." |
|
} |
|
|
|
function Write-IfAbsent($path, $text) { |
|
if (Test-Path $path) { Write-Host " kept existing $(Split-Path $path -Leaf)"; return } |
|
[IO.File]::WriteAllText($path, ($text -replace "`r?`n","`r`n"), [Text.Encoding]::ASCII) |
|
Write-Host " wrote $(Split-Path $path -Leaf)" |
|
} |
|
|
|
# --- config.txt (optional settings; docs inside the file) --- |
|
Write-IfAbsent (Join-Path $root 'config.txt') @' |
|
# ============================================================ |
|
# Claude Code Portable - config (OPTIONAL) |
|
# ============================================================ |
|
# Read by Claude.cmd at launch. Safe to leave fully commented |
|
# out - browser login needs nothing here. Edit any time. |
|
# Syntax: KEY=value (no spaces around =, no quotes, # = comment) |
|
# ============================================================ |
|
|
|
# API-key auth instead of browser login (get one at https://console.anthropic.com/) |
|
#ANTHROPIC_API_KEY=sk-ant-... |
|
|
|
# Route through a proxy/gateway. WARNING: sends every request AND your |
|
# credentials to the host you name. Only ever point it somewhere you own. |
|
#ANTHROPIC_BASE_URL=https://your-gateway.example.com |
|
|
|
# ------------------------------------------------------------ |
|
# Never run a stick - or a config file - given to you by |
|
# someone else. Both can silently redirect your credentials. |
|
# ------------------------------------------------------------ |
|
'@ |
|
|
|
# --- field-IT priming: loaded when Claude.cmd runs from work\ (the default) --- |
|
New-Item -ItemType Directory -Force (Join-Path $root 'work\.claude') | Out-Null |
|
Write-IfAbsent (Join-Path $root 'work\CLAUDE.md') @' |
|
# Windows break-fix technician |
|
|
|
You are an on-site Windows repair assistant running from a technician's USB stick. The |
|
technician is at the keyboard and approves actions. Work fast: diagnose, then fix. |
|
|
|
## Rules |
|
- This is Windows. Use PowerShell (and cmd where simpler). Never assume bash / Unix tools. |
|
- Diagnose before you change. State the one-line cause you suspect, then act. |
|
- Prefer reversible fixes. Before disabling or removing anything, say in one line what it is |
|
and what breaks if you are wrong, then let the confirmation prompt do its job. |
|
- Batch read-only checks, then act. Report findings and changes briefly - no essays. |
|
- When done, give a 2-3 line summary of every change, so it can be undone. |
|
|
|
## Do not, without the owner confirming |
|
- Touch business-critical software, its services, licensing, or database. |
|
- Open, copy, or move sensitive data (customer records, databases, personal files). |
|
You fix the machine, not the data on it. |
|
|
|
## Playbooks (run the sequence, stop when fixed, confirm it worked) |
|
|
|
### Printer will not print |
|
1. Get-Service Spooler -- if stopped, Start-Service Spooler. |
|
2. Stuck queue: stop Spooler, delete C:\Windows\System32\spool\PRINTERS\*, start Spooler. |
|
3. Correct default? Get-Printer -- not defaulting to "Microsoft Print to PDF". |
|
4. Offline flag: check PrinterStatus; clear "Use Printer Offline". |
|
5. Driver: Get-PrinterDriver -- reinstall with pnputil / Add-PrinterDriver if missing. |
|
6. Connection: USB replugged, or network printer reachable (Test-Connection <ip>). |
|
7. Print a Windows test page. |
|
|
|
### "The computer is slow" |
|
1. Startup bloat: Get-CimInstance Win32_StartupCommand -- disable heavy non-essentials |
|
(confirm each; leave AV and business-critical apps alone). |
|
2. Disk space: Get-PSDrive C -- if near full: cleanmgr, clear %TEMP%, empty Recycle Bin. |
|
3. Resource hogs: Get-Process | Sort CPU -Descending | Select -First 10. |
|
4. Pending reboot or a stuck update thrashing the disk -- reboot if so. |
|
5. Malware check: Start-MpScan -ScanType QuickScan (Defender); review. |
|
6. Browser toolbars / excess extensions -- remove with consent. |
|
7. If it is a spinning HDD and thrashing, note that an SSD is the real fix. |
|
|
|
### No internet |
|
1. Get-NetAdapter (up?) and ipconfig /all (IP + gateway?). |
|
2. Test-Connection 1.1.1.1 vs Test-Connection google.com to isolate DNS. |
|
3. Clear-DnsClientCache; set a working resolver if theirs is dead. |
|
4. ipconfig /release then /renew. If needed: netsh winsock reset (reboot after). |
|
5. Adapter driver / Wi-Fi radio / cable as applicable. |
|
|
|
### Unknown error |
|
Read the message verbatim, search the exact text, apply the known fix, verify. |
|
'@ |
|
|
|
# --- safe-fast permissions: read-only triage silent, changes still prompt --- |
|
Write-IfAbsent (Join-Path $root 'work\.claude\settings.json') @' |
|
{ |
|
"$schema": "https://json.schemastore.org/claude-code-settings.json", |
|
"permissions": { |
|
"defaultMode": "default", |
|
"allow": [ |
|
"Bash(Get-Service:*)", |
|
"Bash(Get-Process:*)", |
|
"Bash(Get-Printer:*)", |
|
"Bash(Get-PrinterDriver:*)", |
|
"Bash(Get-CimInstance:*)", |
|
"Bash(Get-NetAdapter:*)", |
|
"Bash(Get-PSDrive:*)", |
|
"Bash(Test-Connection:*)", |
|
"Bash(systeminfo:*)", |
|
"Bash(ipconfig:*)" |
|
], |
|
"deny": [ |
|
"Bash(format:*)", |
|
"Bash(diskpart:*)", |
|
"Bash(Clear-Disk:*)", |
|
"Bash(Format-Volume:*)", |
|
"Bash(bcdedit:*)" |
|
], |
|
"disableBypassPermissionsMode": "disable" |
|
} |
|
} |
|
'@ |
|
|
|
# --- write the INERT, self-elevating launcher (no download path; that is the point) --- |
|
$launcher = @' |
|
@echo off |
|
REM Claude Code Portable - launcher (generated by InstallClaude.cmd). |
|
REM Inert by design: no download path. Self-elevates so admin fixes work. |
|
REM If bin\claude.exe is missing, re-run InstallClaude.cmd on a trusted machine. |
|
REM VERIFY=0 skips the publisher check. |
|
setlocal |
|
set "ROOT=%~dp0" |
|
set "VERIFY=1" |
|
|
|
REM --- self-elevate so admin fixes (spooler, drivers, services) work --- |
|
net session >nul 2>&1 |
|
if errorlevel 1 ( |
|
powershell -NoProfile -Command "try{Start-Process -FilePath '%~f0' -ArgumentList '%*' -Verb RunAs -ErrorAction Stop}catch{exit 1}" |
|
if not errorlevel 1 exit /b |
|
) |
|
|
|
set "CLAUDE_CONFIG_DIR=%ROOT%data" |
|
set "CLAUDE_CODE_TMPDIR=%ROOT%tmp" |
|
set "DISABLE_AUTOUPDATER=1" |
|
set "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1" |
|
if exist "%ProgramFiles%\Git\bin\bash.exe" set "CLAUDE_CODE_GIT_BASH_PATH=%ProgramFiles%\Git\bin\bash.exe" |
|
|
|
if exist "%ROOT%config.txt" for /f "usebackq eol=# tokens=1,* delims==" %%A in ("%ROOT%config.txt") do set "%%A=%%B" |
|
|
|
set "BINEXE=%ROOT%bin\claude.exe" |
|
if not exist "%BINEXE%" goto :nobin |
|
md "%CLAUDE_CONFIG_DIR%" 2>nul & md "%CLAUDE_CODE_TMPDIR%" 2>nul & md "%ROOT%work" 2>nul |
|
|
|
if "%VERIFY%"=="1" powershell -NoProfile -Command "$s=Get-AuthenticodeSignature \"$env:BINEXE\"; if($s.Status -ne 'Valid' -or $s.SignerCertificate.Subject -notlike '*Anthropic*'){exit 1}" || goto :badsig |
|
|
|
REM --- stable virtual drive so sessions resume regardless of the stick's real letter --- |
|
REM Claude keys session history by the working-dir path (drive letter included), so an |
|
REM E: vs F: mount orphans your resumes. Map a fixed letter onto the stick so cwd never |
|
REM changes between machines. Override the letter with PORTABLE_DRIVE=X: in config.txt. |
|
set "VDRIVE=Q:" |
|
if defined PORTABLE_DRIVE set "VDRIVE=%PORTABLE_DRIVE%" |
|
set "MADEVDRIVE=" |
|
if not exist "%VDRIVE%\" ( |
|
subst %VDRIVE% "%ROOT:~0,-1%" >nul 2>&1 |
|
if not errorlevel 1 set "MADEVDRIVE=1" |
|
) |
|
set "BASE=%ROOT%" |
|
if exist "%VDRIVE%\bin\claude.exe" (set "BASE=%VDRIVE%\") else echo [note] %VDRIVE% unavailable - using %ROOT%; sessions may not resume this time. |
|
|
|
set "WORK=%BASE%work" & set "ARGS=%*" |
|
if not "%~1"=="" if exist "%~1\" (set "WORK=%~f1" & set "ARGS=") |
|
cd /d "%WORK%" && "%BINEXE%" %ARGS% |
|
set "RC=%errorlevel%" |
|
if defined MADEVDRIVE subst %VDRIVE% /d >nul 2>&1 |
|
exit /b %RC% |
|
|
|
:nobin |
|
echo. |
|
echo bin\claude.exe is not on this stick, and this launcher never downloads. |
|
echo Re-run InstallClaude.cmd once on a machine you trust, then use this. |
|
echo. |
|
pause & exit /b 1 |
|
|
|
:badsig |
|
echo. |
|
echo *** PUBLISHER CHECK FAILED *** |
|
echo bin\claude.exe is not validly signed by Anthropic, PBC - or PowerShell is |
|
echo restricted on this host and the check could not run. Either way, stop and |
|
echo re-run InstallClaude.cmd from a clean copy on a machine you trust. |
|
echo. |
|
pause & exit /b 1 |
|
'@ |
|
[IO.File]::WriteAllText((Join-Path $root 'Claude.cmd'), ($launcher -replace "`r?`n","`r`n"), [Text.Encoding]::ASCII) |
|
Write-Host " wrote Claude.cmd (inert, self-elevating)" |