From 1191123602b9a8bf13aca32035f20d0e644ec68d Mon Sep 17 00:00:00 2001 From: Administrator Date: Mon, 13 Apr 2026 14:27:45 -0700 Subject: [PATCH] sync: Neptune Exchange session - domain cleanup, SBR routing, Mailprotector config, AD remediation Machine: NEPTUNE Timestamp: 2026-04-13 14:28:00 Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 222 ++++++++++++++ bootstrap.ps1 | 810 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1032 insertions(+) create mode 100644 README.md create mode 100644 bootstrap.ps1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa4c164 --- /dev/null +++ b/README.md @@ -0,0 +1,222 @@ +# ClaudeTools Bootstrap / Reinstall Guide + +Complete instructions for backing up and restoring a ClaudeTools development environment on Windows 11. + +--- + +## Pre-Reinstall: Creating the Archive + +Before wiping or reinstalling Windows, create a backup archive. + +### Option A: Automated Archive (Recommended) + +Run the bootstrap script in archive mode: + +```powershell +cd D:\ClaudeTools\bootstrap +.\bootstrap.ps1 -Archive +``` + +This creates `D:\ClaudeTools-backup.zip` containing: +- The full ClaudeTools repository (excluding `node_modules`, `__pycache__`, `venv`) +- Claude configuration and memory from `C:\Users\\.claude\` + +To specify a custom output path: + +```powershell +.\bootstrap.ps1 -Archive -ArchivePath "E:\Backups\claudetools-2026-03-17.zip" +``` + +### Option B: Manual Archive + +If the script is unavailable, manually zip these locations: + +1. **ClaudeTools repository**: `D:\ClaudeTools\` (entire directory) +2. **Claude memory and config**: `C:\Users\\.claude\` (entire directory) + +Copy the archive(s) to external storage (USB, NAS, cloud) before proceeding with the Windows reinstall. + +### What Does NOT Need Archiving + +These are restored automatically by the bootstrap script: +- Git, Node.js, Python, Ollama (reinstalled via winget) +- npm global packages (reinstalled) +- Python pip packages (reinstalled) +- Ollama models (re-pulled) +- MCP server virtual environments (recreated) + +--- + +## Post-Reinstall: Running the Bootstrap + +### Step 1: Prepare the D: Drive + +If `D:\ClaudeTools` was on a separate partition that survived the reinstall, skip to Step 2. + +Otherwise, extract your archive: + +```powershell +# Extract the ClaudeTools repo to D:\ +Expand-Archive -Path "E:\Backups\claudetools-2026-03-17.zip" -DestinationPath "D:\" + +# Extract Claude config to your user profile +# (The archive contains a 'claude-config' folder - copy it to the right place) +Copy-Item -Path "D:\claude-config\*" -Destination "$env:USERPROFILE\.claude\" -Recurse -Force +``` + +### Step 2: Run the Bootstrap Script + +Open an **elevated PowerShell** (Run as Administrator): + +```powershell +Set-ExecutionPolicy Bypass -Scope Process -Force +D:\ClaudeTools\bootstrap\bootstrap.ps1 +``` + +The script runs 9 phases and takes approximately 15-30 minutes depending on download speeds and Ollama model sizes. + +### Step 3: Advanced Usage + +Run a single phase: + +```powershell +.\bootstrap.ps1 -OnlyPhase 4 # Only install Python packages +``` + +Skip specific phases: + +```powershell +.\bootstrap.ps1 -SkipPhase 5 # Skip Ollama model pulls (slow) +.\bootstrap.ps1 -SkipPhase 4,5 # Skip Python packages and Ollama models +``` + +--- + +## Phase Reference + +| Phase | What It Does | Duration | +|-------|-------------|----------| +| 1 | Install Git, Node.js, Python 3.13, Ollama via winget | 2-5 min | +| 2 | Install Claude Code CLI + global npm packages | 1-2 min | +| 3 | Clone or configure ClaudeTools git repository | <1 min | +| 4 | Install all Python pip packages globally | 3-5 min | +| 5 | Pull Ollama models (nomic-embed-text, llama3.1:8b, qwen2.5-coder:7b) | 5-15 min | +| 6 | Create MCP server venv and install dependencies | 1-2 min | +| 7 | Write Claude Code settings.json, copy commands, create directories | <1 min | +| 8 | Initialize GrepAI | <1 min | +| 9 | Verify all components are installed and working | <1 min | + +--- + +## Manual Steps (Cannot Be Automated) + +These steps require interactive authentication or browser actions: + +### 1. Authenticate Claude Code + +```powershell +claude +``` + +Follow the prompts to enter your Anthropic API key or log in via browser. + +### 2. GitHub Personal Access Token + +Edit `D:\ClaudeTools\.mcp.json` and set the `GITHUB_PERSONAL_ACCESS_TOKEN` value: + +```json +"env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token_here" +} +``` + +Generate a new token at: https://github.com/settings/tokens + +### 3. Claude-in-Chrome Extension + +Install the Chrome extension manually: +- Open Chrome and navigate to the Chrome Web Store +- Search for "Claude in Chrome" (or install from the MCP extension source) +- Configure the extension to connect to your local MCP server + +### 4. Restore Memory Files (If Needed) + +If the bootstrap reports memory files are missing: + +```powershell +# Copy from your archive +Copy-Item -Path "E:\Backups\claude-config\projects\D--ClaudeTools\memory\*" ` + -Destination "$env:USERPROFILE\.claude\projects\D--ClaudeTools\memory\" ` + -Recurse -Force +``` + +### 5. Git Credentials + +When you first `git pull` or `git push` to Gitea, you will be prompted for credentials. Use the Gitea username and password from `credentials.md`. + +--- + +## Verification Checklist + +After bootstrap completes, verify manually: + +- [ ] `git --version` returns a version +- [ ] `node --version` returns v24.x or later +- [ ] `python --version` returns 3.13.x +- [ ] `claude --version` returns a version +- [ ] `ollama list` shows all 3 models +- [ ] `D:\ClaudeTools` exists and has `.git` directory +- [ ] `D:\ClaudeTools\.mcp.json` exists +- [ ] `D:\ClaudeTools\grepai.exe` exists +- [ ] `C:\Users\\.claude\settings.json` exists +- [ ] `C:\Users\\.claude\commands\` has command files +- [ ] Run `claude` from `D:\ClaudeTools` and confirm MCP servers connect +- [ ] Claude-in-Chrome extension is installed and responsive + +--- + +## Troubleshooting + +### winget not found +Install "App Installer" from the Microsoft Store. It ships with Windows 11 but may need updating. + +### Node.js/Python not on PATH after install +Close and reopen your terminal, or run: +```powershell +$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") +``` + +### Ollama models fail to pull +Ensure the Ollama service is running: +```powershell +ollama serve +``` +Then retry: +```powershell +.\bootstrap.ps1 -OnlyPhase 5 +``` + +### pip install fails for specific packages +Some packages (pywin32, opencv-python, pyzbar) require Visual C++ Build Tools. Install if needed: +```powershell +winget install Microsoft.VisualStudio.2022.BuildTools +``` +Then re-run Phase 4: +```powershell +.\bootstrap.ps1 -OnlyPhase 4 +``` + +### GrepAI init requires interaction +Run manually: +```powershell +cd D:\ClaudeTools +.\grepai.exe init +``` +Select Ollama as the provider and nomic-embed-text as the embedding model. + +### SSL certificate errors with Gitea +The bootstrap configures `http.sslVerify false` automatically. If you still see errors: +```powershell +cd D:\ClaudeTools +git config http.sslVerify false +``` diff --git a/bootstrap.ps1 b/bootstrap.ps1 new file mode 100644 index 0000000..1d3ddf6 --- /dev/null +++ b/bootstrap.ps1 @@ -0,0 +1,810 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + ClaudeTools Bootstrap / Reinstall Script +.DESCRIPTION + One-and-done script to restore a complete ClaudeTools development environment + after a fresh Windows 11 install. Idempotent - safe to re-run at any time. +.NOTES + Run from an elevated PowerShell prompt: + Set-ExecutionPolicy Bypass -Scope Process -Force + .\bootstrap.ps1 + + Optional flags: + -SkipPhase Skip a specific phase (1-9) + -OnlyPhase Run only a specific phase (1-9) + -Archive Create pre-reinstall archive instead of installing + -ArchivePath Custom archive output path (default: D:\ClaudeTools-backup.zip) +#> + +param( + [int[]]$SkipPhase = @(), + [int]$OnlyPhase = 0, + [switch]$Archive, + [string]$ArchivePath = "D:\ClaudeTools-backup.zip" +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Continue" + +# --------------------------------------------------------------------------- +# Globals +# --------------------------------------------------------------------------- +$Script:ClaudeToolsRoot = "D:\ClaudeTools" +$Script:GiteaRepo = "https://git.azcomputerguru.com/azcomputerguru/claudetools.git" +$Script:ClaudeConfigDir = Join-Path $env:USERPROFILE ".claude" +$Script:ClaudeCommandsDir = Join-Path $Script:ClaudeConfigDir "commands" +$Script:MemoryDir = Join-Path $Script:ClaudeConfigDir "projects\D--ClaudeTools\memory" +$Script:Errors = [System.Collections.Generic.List[string]]::new() +$Script:Warnings = [System.Collections.Generic.List[string]]::new() +$Script:Successes = [System.Collections.Generic.List[string]]::new() +$Script:Skipped = [System.Collections.Generic.List[string]]::new() + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +function Write-Status { + param( + [string]$Prefix, + [string]$Message, + [ConsoleColor]$Color = [ConsoleColor]::White + ) + Write-Host "[$Prefix] " -ForegroundColor $Color -NoNewline + Write-Host $Message +} + +function Write-OK { param([string]$Msg) Write-Status "OK" $Msg Green; $Script:Successes.Add($Msg) } +function Write-Err { param([string]$Msg) Write-Status "ERROR" $Msg Red; $Script:Errors.Add($Msg) } +function Write-Warn { param([string]$Msg) Write-Status "WARNING" $Msg Yellow; $Script:Warnings.Add($Msg) } +function Write-Skip { param([string]$Msg) Write-Status "SKIP" $Msg Cyan; $Script:Skipped.Add($Msg) } +function Write-Info { param([string]$Msg) Write-Status "INFO" $Msg White } +function Write-Phase { param([int]$Num, [string]$Title) + Write-Host "" + Write-Host ("=" * 70) -ForegroundColor Magenta + Write-Host " Phase ${Num}: $Title" -ForegroundColor Magenta + Write-Host ("=" * 70) -ForegroundColor Magenta + Write-Host "" +} + +function Test-CommandExists { + param([string]$Command) + $null -ne (Get-Command $Command -ErrorAction SilentlyContinue) +} + +function Refresh-PathEnv { + # Reload PATH from registry so newly-installed tools are visible + $machinePath = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + $userPath = [System.Environment]::GetEnvironmentVariable("Path", "User") + $env:Path = "$machinePath;$userPath" +} + +function Install-WingetPackage { + param( + [string]$PackageId, + [string]$FriendlyName, + [string]$VersionHint = "" + ) + + # Check if already installed via winget list + $installed = winget list --id $PackageId 2>$null + if ($LASTEXITCODE -eq 0 -and $installed -match $PackageId) { + Write-Skip "$FriendlyName is already installed" + return $true + } + + Write-Info "Installing $FriendlyName ($PackageId)..." + $args = @("install", "--id", $PackageId, "--accept-source-agreements", "--accept-package-agreements", "--silent") + if ($VersionHint) { + $args += @("--version", $VersionHint) + } + $result = & winget @args 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-OK "$FriendlyName installed successfully" + return $true + } else { + # winget sometimes returns non-zero even on success (already installed race) + $resultText = $result -join "`n" + if ($resultText -match "already installed" -or $resultText -match "No applicable update") { + Write-Skip "$FriendlyName is already installed" + return $true + } + Write-Err "Failed to install $FriendlyName : $resultText" + return $false + } +} + +function ShouldRunPhase { + param([int]$PhaseNum) + if ($OnlyPhase -gt 0) { return $PhaseNum -eq $OnlyPhase } + return $PhaseNum -notin $SkipPhase +} + +# --------------------------------------------------------------------------- +# Archive Mode: Create pre-reinstall backup +# --------------------------------------------------------------------------- +function Invoke-Archive { + Write-Phase 0 "Creating Pre-Reinstall Archive" + + $tempStaging = Join-Path $env:TEMP "claudetools-archive-staging" + if (Test-Path $tempStaging) { Remove-Item $tempStaging -Recurse -Force } + New-Item -ItemType Directory -Path $tempStaging -Force | Out-Null + + # Create subdirectories in staging + $ctDest = Join-Path $tempStaging "ClaudeTools" + $memDest = Join-Path $tempStaging "claude-memory" + + # Copy ClaudeTools repo (exclude node_modules, __pycache__, venv, .git large objects) + Write-Info "Copying ClaudeTools repository..." + if (Test-Path $Script:ClaudeToolsRoot) { + $robocopyArgs = @( + $Script:ClaudeToolsRoot, $ctDest, + "/E", "/NFL", "/NDL", "/NJH", "/NJS", + "/XD", "node_modules", "__pycache__", "venv", ".venv", ".mypy_cache" + ) + & robocopy @robocopyArgs | Out-Null + Write-OK "ClaudeTools repository copied" + } else { + Write-Err "ClaudeTools directory not found at $Script:ClaudeToolsRoot" + } + + # Copy Claude memory/config + Write-Info "Copying Claude configuration and memory..." + if (Test-Path $Script:ClaudeConfigDir) { + $memSrc = $Script:ClaudeConfigDir + & robocopy $memSrc (Join-Path $tempStaging "claude-config") /E /NFL /NDL /NJH /NJS /XD "node_modules" | Out-Null + Write-OK "Claude configuration copied" + } else { + Write-Warn "Claude config directory not found at $Script:ClaudeConfigDir" + } + + # Compress + Write-Info "Compressing archive to $ArchivePath ..." + if (Test-Path $ArchivePath) { Remove-Item $ArchivePath -Force } + Compress-Archive -Path "$tempStaging\*" -DestinationPath $ArchivePath -CompressionLevel Optimal + Write-OK "Archive created: $ArchivePath" + + # Cleanup staging + Remove-Item $tempStaging -Recurse -Force + + $sizeMB = [math]::Round((Get-Item $ArchivePath).Length / 1MB, 1) + Write-Host "" + Write-OK "Archive complete: $ArchivePath ($sizeMB MB)" + Write-Info "Copy this file to external storage before reinstalling Windows." +} + +# --------------------------------------------------------------------------- +# Phase 1: Prerequisites +# --------------------------------------------------------------------------- +function Invoke-Phase1 { + Write-Phase 1 "Prerequisites Installation (winget)" + + if (-not (Test-CommandExists "winget")) { + Write-Err "winget is not available. Please install App Installer from the Microsoft Store." + return + } + + Install-WingetPackage "Git.Git" "Git" + Install-WingetPackage "OpenJS.NodeJS" "Node.js (Latest LTS)" + Install-WingetPackage "Python.Python.3.13" "Python 3.13" + Install-WingetPackage "Ollama.Ollama" "Ollama" + + # Refresh PATH so subsequent phases see the new installs + Refresh-PathEnv + + # Verify critical tools are now on PATH + foreach ($tool in @("git", "node", "python", "ollama")) { + if (Test-CommandExists $tool) { + $ver = & $tool --version 2>&1 | Select-Object -First 1 + Write-OK "$tool found: $ver" + } else { + Write-Warn "$tool not found on PATH after install. You may need to restart your terminal." + } + } +} + +# --------------------------------------------------------------------------- +# Phase 2: Claude Code + Global NPM Packages +# --------------------------------------------------------------------------- +function Invoke-Phase2 { + Write-Phase 2 "Claude Code and Global NPM Packages" + + if (-not (Test-CommandExists "npm")) { + Refresh-PathEnv + if (-not (Test-CommandExists "npm")) { + Write-Err "npm not found. Node.js installation may have failed." + return + } + } + + $npmPackages = @( + @{ Name = "@anthropic-ai/claude-code"; Cmd = "claude" }, + @{ Name = "clawhub"; Cmd = "clawhub" }, + @{ Name = "mcporter"; Cmd = "mcporter" }, + @{ Name = "openclaw"; Cmd = "openclaw" } + ) + + foreach ($pkg in $npmPackages) { + # Check if already installed globally + $installed = npm list -g $($pkg.Name) --depth=0 2>$null + if ($LASTEXITCODE -eq 0 -and $installed -match $pkg.Name) { + Write-Skip "$($pkg.Name) is already installed globally" + } else { + Write-Info "Installing $($pkg.Name) globally..." + npm install -g $($pkg.Name) 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-OK "$($pkg.Name) installed" + } else { + Write-Err "Failed to install $($pkg.Name)" + } + } + } +} + +# --------------------------------------------------------------------------- +# Phase 3: Clone or Configure ClaudeTools Repository +# --------------------------------------------------------------------------- +function Invoke-Phase3 { + Write-Phase 3 "ClaudeTools Repository" + + if (-not (Test-CommandExists "git")) { + Refresh-PathEnv + if (-not (Test-CommandExists "git")) { + Write-Err "git not found. Cannot proceed with repository setup." + return + } + } + + if (Test-Path (Join-Path $Script:ClaudeToolsRoot ".git")) { + Write-Skip "ClaudeTools repository already exists at $Script:ClaudeToolsRoot" + Write-Info "Configuring git for self-signed certificate..." + Push-Location $Script:ClaudeToolsRoot + git config http.sslVerify false 2>&1 | Out-Null + Pop-Location + Write-OK "Git SSL verification disabled for self-signed Gitea cert" + + # Ensure remote is correct + Push-Location $Script:ClaudeToolsRoot + $currentRemote = git remote get-url origin 2>$null + if ($currentRemote -ne $Script:GiteaRepo) { + git remote set-url origin $Script:GiteaRepo 2>&1 | Out-Null + Write-OK "Git remote updated to $Script:GiteaRepo" + } else { + Write-OK "Git remote is correct" + } + Pop-Location + } + elseif (Test-Path $Script:ClaudeToolsRoot) { + # Directory exists but is not a git repo (restored from archive) + Write-Info "Directory exists but is not a git repo. Initializing..." + Push-Location $Script:ClaudeToolsRoot + git init 2>&1 | Out-Null + git remote add origin $Script:GiteaRepo 2>&1 | Out-Null + git config http.sslVerify false 2>&1 | Out-Null + git fetch origin 2>&1 | Out-Null + git checkout -B main origin/main 2>&1 | Out-Null + Pop-Location + Write-OK "Repository initialized from archive and connected to remote" + } + else { + # Fresh clone + Write-Info "Cloning ClaudeTools repository..." + # Ensure D:\ exists + if (-not (Test-Path "D:\")) { + Write-Err "D:\ drive not found. Cannot clone repository." + return + } + git clone -c http.sslVerify=false $Script:GiteaRepo $Script:ClaudeToolsRoot 2>&1 + if ($LASTEXITCODE -eq 0) { + Push-Location $Script:ClaudeToolsRoot + git config http.sslVerify false 2>&1 | Out-Null + Pop-Location + Write-OK "Repository cloned successfully" + } else { + Write-Err "Failed to clone repository" + } + } +} + +# --------------------------------------------------------------------------- +# Phase 4: Python Environment +# --------------------------------------------------------------------------- +function Invoke-Phase4 { + Write-Phase 4 "Python Packages (Global pip install)" + + if (-not (Test-CommandExists "python")) { + Refresh-PathEnv + if (-not (Test-CommandExists "python")) { + Write-Err "python not found. Python installation may have failed." + return + } + } + + $pyVersion = python --version 2>&1 + Write-OK "Python detected: $pyVersion" + + # Full list of required packages with pinned versions + $pipPackages = @( + "alembic==1.13.1", + "annotated-types==0.7.0", + "anyio==4.12.1", + "argon2-cffi==25.1.0", + "argon2-cffi-bindings==25.1.0", + "bcrypt==5.0.0", + "beautifulsoup4==4.13.5", + "certifi==2026.1.4", + "cffi==2.0.0", + "claude-agent-sdk==0.1.19", + "claude-code-sdk==0.0.25", + "click==8.3.1", + "colorama==0.4.6", + "compressed-rtf==1.0.6", + "cryptography==46.0.3", + "easygui==0.98.3", + "extract-msg==0.55.0", + "fastapi==0.128.0", + "google-api-core==2.30.0", + "google-api-python-client==2.192.0", + "google-auth==2.49.0", + "google-auth-httplib2==0.3.0", + "googleapis-common-protos==1.73.0", + "greenlet==3.3.0", + "httpcore==1.0.9", + "httplib2==0.31.2", + "httpx==0.28.1", + "httpx-sse==0.4.3", + "invoke==2.2.1", + "jsonschema==4.26.0", + "lark==1.3.1", + "Mako==1.3.10", + "MarkupSafe==3.0.3", + "mcp==1.25.0", + "msoffcrypto-tool==5.4.2", + "numpy==2.4.2", + "olefile==0.47", + "oletools==0.60.2", + "opencv-python==4.13.0.90", + "paramiko==4.0.0", + "passlib==1.7.4", + "pillow==12.1.0", + "proto-plus==1.27.1", + "protobuf==6.33.5", + "pydantic==2.12.5", + "pydantic-settings==2.12.0", + "PyJWT==2.10.1", + "PyMySQL==1.1.0", + "PyNaCl==1.6.2", + "python-dotenv==1.2.1", + "python-multipart==0.0.21", + "pywin32==311", + "pyzbar==0.1.9", + "requests==2.32.5", + "RTFDE==0.1.2.2", + "SQLAlchemy==2.0.45", + "sse-starlette==3.1.2", + "starlette==0.50.0", + "tzdata==2025.3", + "tzlocal==5.3.1", + "uritemplate==4.2.0", + "uvicorn==0.40.0", + "uv==0.9.9", + "websockets==15.0.1" + ) + + # Write a temporary requirements file for batch install + $reqFile = Join-Path $env:TEMP "claudetools-requirements.txt" + $pipPackages | Out-File -FilePath $reqFile -Encoding UTF8 + + Write-Info "Installing $($pipPackages.Count) Python packages (this may take several minutes)..." + $pipOutput = python -m pip install --upgrade pip 2>&1 + $pipOutput = python -m pip install -r $reqFile 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-OK "All Python packages installed successfully" + } else { + # Check for partial failures + $failLines = ($pipOutput | Select-String "ERROR:" | ForEach-Object { $_.Line }) + if ($failLines) { + foreach ($line in $failLines) { + Write-Err "pip: $line" + } + } else { + Write-Warn "pip exited with warnings but may have succeeded. Review output above." + } + } + + Remove-Item $reqFile -Force -ErrorAction SilentlyContinue +} + +# --------------------------------------------------------------------------- +# Phase 5: Ollama Models +# --------------------------------------------------------------------------- +function Invoke-Phase5 { + Write-Phase 5 "Ollama Models" + + if (-not (Test-CommandExists "ollama")) { + Refresh-PathEnv + if (-not (Test-CommandExists "ollama")) { + Write-Err "ollama not found. Ollama installation may have failed." + return + } + } + + # Ensure Ollama service is running + Write-Info "Ensuring Ollama service is running..." + $ollamaProcess = Get-Process "ollama" -ErrorAction SilentlyContinue + if (-not $ollamaProcess) { + Write-Info "Starting Ollama service..." + Start-Process "ollama" -ArgumentList "serve" -WindowStyle Hidden + Start-Sleep -Seconds 3 + } + + $models = @( + "nomic-embed-text", + "llama3.1:8b", + "qwen2.5-coder:7b" + ) + + foreach ($model in $models) { + Write-Info "Pulling model: $model (this may take a while)..." + $pullOutput = ollama pull $model 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-OK "Model $model pulled successfully" + } else { + $outputText = $pullOutput -join "`n" + if ($outputText -match "up to date") { + Write-Skip "Model $model is already up to date" + } else { + Write-Err "Failed to pull model $model : $outputText" + } + } + } +} + +# --------------------------------------------------------------------------- +# Phase 6: MCP Server Setup +# --------------------------------------------------------------------------- +function Invoke-Phase6 { + Write-Phase 6 "MCP Server Setup (Ollama Assistant)" + + $ollamaAssistantDir = Join-Path $Script:ClaudeToolsRoot "mcp-servers\ollama-assistant" + $venvDir = Join-Path $ollamaAssistantDir "venv" + $venvPython = Join-Path $venvDir "Scripts\python.exe" + $requirementsFile = Join-Path $ollamaAssistantDir "requirements.txt" + + if (-not (Test-Path $ollamaAssistantDir)) { + Write-Err "Ollama assistant directory not found at $ollamaAssistantDir" + return + } + + # Create venv if it doesn't exist or is broken + if (-not (Test-Path $venvPython)) { + Write-Info "Creating Python virtual environment..." + if (Test-Path $venvDir) { Remove-Item $venvDir -Recurse -Force } + python -m venv $venvDir 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-OK "Virtual environment created" + } else { + Write-Err "Failed to create virtual environment" + return + } + } else { + Write-Skip "Virtual environment already exists" + } + + # Install requirements + Write-Info "Installing MCP server dependencies..." + if (Test-Path $requirementsFile) { + & $venvPython -m pip install --upgrade pip 2>&1 | Out-Null + $installOutput = & $venvPython -m pip install -r $requirementsFile 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-OK "MCP server dependencies installed" + } else { + Write-Err "Failed to install MCP server dependencies: $installOutput" + } + } else { + # Fallback: install mcp and httpx directly + & $venvPython -m pip install --upgrade pip 2>&1 | Out-Null + & $venvPython -m pip install "mcp>=0.1.0" "httpx>=0.25.0" 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-OK "MCP server dependencies installed (fallback)" + } else { + Write-Err "Failed to install MCP server dependencies" + } + } + + # Verify .mcp.json exists in repo + $mcpJson = Join-Path $Script:ClaudeToolsRoot ".mcp.json" + if (Test-Path $mcpJson) { + Write-OK ".mcp.json configuration found in repository" + } else { + Write-Warn ".mcp.json not found - MCP servers will not be configured" + } +} + +# --------------------------------------------------------------------------- +# Phase 7: Claude Code Configuration +# --------------------------------------------------------------------------- +function Invoke-Phase7 { + Write-Phase 7 "Claude Code Configuration" + + # Create directories + foreach ($dir in @($Script:ClaudeConfigDir, $Script:ClaudeCommandsDir, $Script:MemoryDir)) { + if (-not (Test-Path $dir)) { + New-Item -ItemType Directory -Path $dir -Force | Out-Null + Write-OK "Created directory: $dir" + } else { + Write-Skip "Directory exists: $dir" + } + } + + # Write settings.json + $settingsPath = Join-Path $Script:ClaudeConfigDir "settings.json" + $settingsContent = @' +{ + "permissions": { + "allow": [ + "Bash(git:*)", "Bash(gh:*)", "Bash(ssh:*)", "Bash(scp:*)", "Bash(rsync:*)", + "Bash(wsl:*)", "Bash(wsl.exe:*)", "Bash(cat:*)", "Bash(ls:*)", "Bash(find:*)", + "Bash(grep:*)", "Bash(echo:*)", "Bash(chmod:*)", "Bash(chown:*)", "Bash(mkdir:*)", + "Bash(rm:*)", "Bash(cp:*)", "Bash(mv:*)", "Bash(curl:*)", "Bash(wget:*)", + "Bash(nslookup:*)", "Bash(dig:*)", "Bash(ping:*)", "Bash(python:*)", "Bash(python3:*)", + "Bash(node:*)", "Bash(npm:*)", "Bash(npx:*)", "Bash(cargo:*)", "Bash(rustc:*)", + "Bash(rustup:*)", "Bash(powershell:*)", "Bash(powershell.exe:*)", "Bash(pwsh:*)", + "Bash(which:*)", "Bash(where:*)", "Bash(whoami:*)", "Bash(date:*)", "Bash(head:*)", + "Bash(tail:*)", "Bash(less:*)", "Bash(more:*)", "Bash(diff:*)", "Bash(tar:*)", + "Bash(unzip:*)", "Bash(zip:*)", "Bash(docker:*)", "Bash(docker-compose:*)", + "Bash(systemctl:*)", "Bash(service:*)", "Bash(journalctl:*)", "Bash(apt:*)", + "Bash(apt-get:*)", "Bash(brew:*)", "Bash(code:*)", "Bash(make:*)", "Bash(cmake:*)", + "Bash(dir:*)", "Bash(wc:*)", "Bash(winget:*)", "Bash(choco:*)", "Bash(ipconfig:*)", + "Bash(net:*)", "Bash(perl:*)", "Bash(xxd:*)", "Bash(timeout:*)", "Bash(claude:*)", + "Bash(plink:*)", "WebFetch(domain:*)", "Skill(s)", + "Read(//c/Users/$env:USERNAME/.claude/**)" + ], + "deny": [], + "ask": [] + }, + "statusLine": { + "type": "command", + "command": "input=$(cat); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); [ -n \"$remaining\" ] && printf \"Context: %.0f%% remaining\" \"$remaining\" || echo \"\"" + }, + "skipDangerousModePermissionPrompt": true +} +'@ + + # Replace $env:USERNAME placeholder with actual username + $settingsContent = $settingsContent -replace '\$env:USERNAME', $env:USERNAME + + Set-Content -Path $settingsPath -Value $settingsContent -Encoding UTF8 -Force + Write-OK "settings.json written to $settingsPath" + + # Copy commands from repo to global + $repoCommandsDir = Join-Path $Script:ClaudeToolsRoot ".claude\commands" + if (Test-Path $repoCommandsDir) { + $commandFiles = Get-ChildItem -Path $repoCommandsDir -File + if ($commandFiles.Count -gt 0) { + Copy-Item -Path "$repoCommandsDir\*" -Destination $Script:ClaudeCommandsDir -Force + Write-OK "Copied $($commandFiles.Count) command files to $Script:ClaudeCommandsDir" + } else { + Write-Warn "No command files found in $repoCommandsDir" + } + } else { + Write-Warn "Repo commands directory not found at $repoCommandsDir (run after Phase 3)" + } + + # Check for archived memory to restore + Write-Info "Checking for memory directory..." + if (Test-Path (Join-Path $Script:MemoryDir "MEMORY.md")) { + Write-Skip "Memory files already present" + } else { + Write-Warn "Memory directory is empty. If you have an archive, manually restore:" + Write-Warn " Copy contents to $Script:MemoryDir" + } +} + +# --------------------------------------------------------------------------- +# Phase 8: GrepAI Setup +# --------------------------------------------------------------------------- +function Invoke-Phase8 { + Write-Phase 8 "GrepAI Setup" + + $grepaiExe = Join-Path $Script:ClaudeToolsRoot "grepai.exe" + + if (-not (Test-Path $grepaiExe)) { + Write-Err "grepai.exe not found at $grepaiExe" + Write-Warn "GrepAI binary should be in the repository. Ensure Phase 3 completed successfully." + return + } + + Write-OK "grepai.exe found at $grepaiExe" + + # Check if already initialized + $grepaiConfig = Join-Path $Script:ClaudeToolsRoot ".grepai" + if (Test-Path $grepaiConfig) { + Write-Skip "GrepAI appears to already be initialized (.grepai directory exists)" + } else { + Write-Info "Initializing GrepAI..." + Write-Info "This may prompt for configuration. Accept defaults for Ollama + nomic-embed-text." + Push-Location $Script:ClaudeToolsRoot + & $grepaiExe init 2>&1 + Pop-Location + if ($LASTEXITCODE -eq 0) { + Write-OK "GrepAI initialized" + } else { + Write-Warn "GrepAI init may require manual interaction. Run manually:" + Write-Warn " cd $Script:ClaudeToolsRoot && .\grepai.exe init" + } + } +} + +# --------------------------------------------------------------------------- +# Phase 9: Verification +# --------------------------------------------------------------------------- +function Invoke-Phase9 { + Write-Phase 9 "Verification" + + $checks = @( + @{ Name = "Git"; Cmd = "git --version" }, + @{ Name = "Node.js"; Cmd = "node --version" }, + @{ Name = "npm"; Cmd = "npm --version" }, + @{ Name = "Python"; Cmd = "python --version" }, + @{ Name = "pip"; Cmd = "python -m pip --version" }, + @{ Name = "Ollama"; Cmd = "ollama --version" }, + @{ Name = "Claude Code CLI"; Cmd = "claude --version" } + ) + + Refresh-PathEnv + + foreach ($check in $checks) { + try { + $result = Invoke-Expression $check.Cmd 2>&1 | Select-Object -First 1 + if ($LASTEXITCODE -eq 0 -or $result) { + Write-OK "$($check.Name): $result" + } else { + Write-Err "$($check.Name): not found or not working" + } + } catch { + Write-Err "$($check.Name): $($_.Exception.Message)" + } + } + + # Check directories + Write-Host "" + Write-Info "Directory checks:" + $dirs = @( + $Script:ClaudeToolsRoot, + $Script:ClaudeConfigDir, + $Script:ClaudeCommandsDir, + (Join-Path $Script:ClaudeToolsRoot "mcp-servers\ollama-assistant\venv") + ) + foreach ($dir in $dirs) { + if (Test-Path $dir) { + Write-OK "EXISTS: $dir" + } else { + Write-Err "MISSING: $dir" + } + } + + # Check key files + Write-Host "" + Write-Info "File checks:" + $files = @( + (Join-Path $Script:ClaudeConfigDir "settings.json"), + (Join-Path $Script:ClaudeToolsRoot ".mcp.json"), + (Join-Path $Script:ClaudeToolsRoot "grepai.exe") + ) + foreach ($file in $files) { + if (Test-Path $file) { + Write-OK "EXISTS: $file" + } else { + Write-Err "MISSING: $file" + } + } + + # Check Ollama models + Write-Host "" + Write-Info "Ollama model checks:" + if (Test-CommandExists "ollama") { + $modelList = ollama list 2>&1 + foreach ($model in @("nomic-embed-text", "llama3.1:8b", "qwen2.5-coder:7b")) { + if ($modelList -match [regex]::Escape($model)) { + Write-OK "Model loaded: $model" + } else { + Write-Warn "Model not found: $model" + } + } + } else { + Write-Warn "Cannot check Ollama models - ollama not on PATH" + } +} + +# --------------------------------------------------------------------------- +# Main Execution +# --------------------------------------------------------------------------- + +Write-Host "" +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host " ClaudeTools Bootstrap / Reinstall Script" -ForegroundColor Cyan +Write-Host " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host "" + +# Archive mode +if ($Archive) { + Invoke-Archive + return +} + +# Check for admin +$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $isAdmin) { + Write-Err "This script requires administrator privileges for winget installations." + Write-Err "Re-run from an elevated PowerShell prompt." + exit 1 +} + +# Execute phases +$phases = @( + @{ Num = 1; Func = { Invoke-Phase1 } }, + @{ Num = 2; Func = { Invoke-Phase2 } }, + @{ Num = 3; Func = { Invoke-Phase3 } }, + @{ Num = 4; Func = { Invoke-Phase4 } }, + @{ Num = 5; Func = { Invoke-Phase5 } }, + @{ Num = 6; Func = { Invoke-Phase6 } }, + @{ Num = 7; Func = { Invoke-Phase7 } }, + @{ Num = 8; Func = { Invoke-Phase8 } }, + @{ Num = 9; Func = { Invoke-Phase9 } } +) + +foreach ($phase in $phases) { + if (ShouldRunPhase $phase.Num) { + try { + & $phase.Func + } catch { + Write-Err "Phase $($phase.Num) failed with exception: $($_.Exception.Message)" + } + } else { + Write-Skip "Phase $($phase.Num) skipped (by request)" + } +} + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +Write-Host "" +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host " Bootstrap Summary" -ForegroundColor Cyan +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host "" + +if ($Script:Successes.Count -gt 0) { + Write-Host " Successes: $($Script:Successes.Count)" -ForegroundColor Green +} +if ($Script:Skipped.Count -gt 0) { + Write-Host " Skipped: $($Script:Skipped.Count)" -ForegroundColor Cyan +} +if ($Script:Warnings.Count -gt 0) { + Write-Host " Warnings: $($Script:Warnings.Count)" -ForegroundColor Yellow + foreach ($w in $Script:Warnings) { + Write-Host " - $w" -ForegroundColor Yellow + } +} +if ($Script:Errors.Count -gt 0) { + Write-Host " Errors: $($Script:Errors.Count)" -ForegroundColor Red + foreach ($e in $Script:Errors) { + Write-Host " - $e" -ForegroundColor Red + } +} + +Write-Host "" +Write-Host " Manual steps remaining:" -ForegroundColor Yellow +Write-Host " 1. Run 'claude' and authenticate with Anthropic API key" -ForegroundColor Yellow +Write-Host " 2. Install Claude-in-Chrome browser extension" -ForegroundColor Yellow +Write-Host " 3. Set GITHUB_PERSONAL_ACCESS_TOKEN in .mcp.json" -ForegroundColor Yellow +Write-Host " 4. Restore memory files if not already present" -ForegroundColor Yellow +Write-Host "" + +if ($Script:Errors.Count -eq 0) { + Write-Host " [OK] Bootstrap completed successfully!" -ForegroundColor Green +} else { + Write-Host " [WARNING] Bootstrap completed with $($Script:Errors.Count) error(s). Review above." -ForegroundColor Yellow +} +Write-Host ""