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
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,32 @@ should tell the agent to read that state and make incremental progress.

## Installation

Install the script to `~/.local/bin`:
The quickest way — a dependency-free installer (needs only `curl` and `tar`)
that copies `ralph-loop.sh` to `~/.local/bin` and installs the bundled skills.
Re-run it any time to update:

```sh
curl -fsSL https://raw.githubusercontent.com/SantanderAI/ralph/main/install.sh | sh
```

It accepts `--no-skills`, `--ref <branch|tag>`, `--repo <owner/name>` and
`--install-dir <dir>` (also available as the `RALPH_SKIP_SKILLS`, `RALPH_REF`,
`RALPH_REPO` and `RALPH_INSTALL_DIR` environment variables), e.g.:

```sh
curl -fsSL https://raw.githubusercontent.com/SantanderAI/ralph/main/install.sh | sh -s -- --no-skills
```

On **Windows / PowerShell** (pwsh 6+), the equivalent installs `ralph-loop.ps1`:

```powershell
powershell -c "irm https://raw.githubusercontent.com/SantanderAI/ralph/main/install.ps1 | iex"
```

The PowerShell installer is configured through the same `RALPH_SKIP_SKILLS`,
`RALPH_REF`, `RALPH_REPO` and `RALPH_INSTALL_DIR` environment variables.

Alternatively, from a clone with [`just`](https://github.com/casey/just):

```sh
just install
Expand Down
111 changes: 111 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env pwsh
# Copyright (c) 2026 César Gallego Rodríguez
# SPDX-License-Identifier: Apache-2.0
#
# ralph installer / updater for PowerShell (pwsh 6+).
#
# Installs ralph-loop.ps1 to ~/.local/bin and copies the bundled skills
# (juez, maestro, ralph) into Claude Code, Codex CLI and Antigravity CLI,
# mirroring install.sh. Re-run it any time to update.
#
# Quick use:
# powershell -c "irm https://raw.githubusercontent.com/SantanderAI/ralph/main/install.ps1 | iex"
#
# Configuration via environment variables (works with the `irm | iex` form):
# RALPH_SKIP_SKILLS Set to 1 to install only the script, skipping skills.
# RALPH_REF Git ref to install from. (default: main)
# RALPH_REPO Source repository. (default: SantanderAI/ralph)
# RALPH_INSTALL_DIR Where to put the script. (default: ~/.local/bin)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$repo = if ($env:RALPH_REPO) { $env:RALPH_REPO } else { 'SantanderAI/ralph' }
$ref = if ($env:RALPH_REF) { $env:RALPH_REF } else { 'main' }
$installDir = if ($env:RALPH_INSTALL_DIR) { $env:RALPH_INSTALL_DIR } else { Join-Path $HOME '.local/bin' }
$skipSkills = [bool]($env:RALPH_SKIP_SKILLS) -and ($env:RALPH_SKIP_SKILLS -ne '0')
$scriptName = 'ralph-loop.ps1'

$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ('ralph-' + [System.Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $tmp | Out-Null
try {
$zip = Join-Path $tmp 'src.zip'
$url = "https://codeload.github.com/$repo/zip/$ref"
Write-Host "Fetching $repo@$ref ..."
try {
Invoke-WebRequest -Uri $url -OutFile $zip
} catch {
throw "Download failed (does ref '$ref' exist in $repo?): $url"
}
Expand-Archive -Path $zip -DestinationPath $tmp -Force
Remove-Item -Path $zip -Force

$src = Get-ChildItem -Path $tmp -Directory | Select-Object -First 1
if (-not $src) { throw 'Unexpected archive layout: no extracted directory.' }

# --- Install the script -------------------------------------------------
$scriptPath = Join-Path $src.FullName $scriptName
if (-not (Test-Path -Path $scriptPath)) {
throw "$scriptName not found in the downloaded archive."
}
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
$dest = Join-Path $installDir $scriptName
Copy-Item -Path $scriptPath -Destination $dest -Force
Write-Host "Installed $dest"

$paths = $env:PATH -split [System.IO.Path]::PathSeparator
if ($paths -notcontains $installDir) {
$sep = [System.IO.Path]::PathSeparator
Write-Host ''
Write-Host "WARNING: $installDir is not in your PATH."
Write-Host 'Add it to your PowerShell profile:'
Write-Host ''
Write-Host " `$env:PATH = `"$installDir$sep`$env:PATH`""
Write-Host ''
}

# --- Install the skills -------------------------------------------------
if ($skipSkills) {
Write-Host 'Skipping skills (RALPH_SKIP_SKILLS).'
return
}

$skillsRoot = Join-Path $src.FullName 'skills'
if (-not (Test-Path -Path $skillsRoot)) {
Write-Host 'No skills/ directory in the archive; nothing else to do.'
return
}

$codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME '.codex' }
$targets = @(
(Join-Path $HOME '.claude/skills'),
(Join-Path $codexHome 'skills'),
(Join-Path $HOME '.gemini/antigravity-cli/skills')
)

$installedAny = $false
foreach ($skillDir in Get-ChildItem -Path $skillsRoot -Directory) {
if (-not (Test-Path -Path (Join-Path $skillDir.FullName 'SKILL.md'))) { continue }
$name = $skillDir.Name
foreach ($target in $targets) {
New-Item -ItemType Directory -Path $target -Force | Out-Null
$skillDest = Join-Path $target $name
if (Test-Path -Path $skillDest) {
Remove-Item -Path $skillDest -Recurse -Force
}
Copy-Item -Path $skillDir.FullName -Destination $skillDest -Recurse -Force
Write-Host " skill $name -> $skillDest"
$installedAny = $true
}
}

if ($installedAny) {
Write-Host ''
Write-Host 'Done. Restart Codex and Antigravity to pick up the skills.'
} else {
Write-Host 'No skills found under skills/<name>/SKILL.md.'
}
}
finally {
Remove-Item -Path $tmp -Recurse -Force -ErrorAction SilentlyContinue
}
125 changes: 125 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/sh
# Copyright (c) 2026 César Gallego Rodríguez
# SPDX-License-Identifier: Apache-2.0
#
# ralph installer / updater — no dependencies beyond curl + tar.
#
# Installs ralph-loop.sh to ~/.local/bin and copies the bundled skills
# (juez, maestro, ralph) into Claude Code, Codex CLI and Antigravity CLI,
# exactly like `just install` + `just skills-install`, but without `just`.
#
# Quick use:
# curl -fsSL https://raw.githubusercontent.com/SantanderAI/ralph/main/install.sh | sh
#
# Options (flags or env vars):
# --no-skills Install only the script, skip skills.
# (env: RALPH_SKIP_SKILLS=1)
# --ref <branch|tag> Git ref to install from. (env: RALPH_REF, default: main)
# --repo <owner/name> Source repository. (env: RALPH_REPO, default: SantanderAI/ralph)
# --install-dir <dir> Where to put the script. (env: RALPH_INSTALL_DIR, default: ~/.local/bin)
#
# Examples:
# curl -fsSL .../install.sh | sh -s -- --no-skills
# RALPH_REF=dev curl -fsSL .../install.sh | sh

set -eu

REPO="${RALPH_REPO:-SantanderAI/ralph}"
REF="${RALPH_REF:-main}"
INSTALL_DIR="${RALPH_INSTALL_DIR:-$HOME/.local/bin}"
SKIP_SKILLS="${RALPH_SKIP_SKILLS:-0}"
SCRIPT="ralph-loop.sh"

while [ $# -gt 0 ]; do
case "$1" in
--no-skills) SKIP_SKILLS=1 ;;
--ref) REF="${2:?--ref needs a value}"; shift ;;
--repo) REPO="${2:?--repo needs a value}"; shift ;;
--install-dir) INSTALL_DIR="${2:?--install-dir needs a value}"; shift ;;
-h|--help) sed -n '2,25p' "$0" 2>/dev/null || echo "See header of install.sh"; exit 0 ;;
*) echo "Unknown option: $1" >&2; exit 2 ;;
esac
shift
done

for dep in curl tar; do
command -v "$dep" >/dev/null 2>&1 || { echo "Error: '$dep' is required." >&2; exit 1; }
done

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT INT TERM

echo "Fetching $REPO@$REF ..."
url="https://codeload.github.com/$REPO/tar.gz/$REF"
if ! curl -fsSL "$url" -o "$tmp/src.tar.gz"; then
echo "Error: download failed (does ref '$REF' exist in $REPO?): $url" >&2
exit 1
fi
if ! tar xzf "$tmp/src.tar.gz" -C "$tmp"; then
echo "Error: could not extract the downloaded archive." >&2
exit 1
fi
rm -f "$tmp/src.tar.gz"

src="$tmp/$(ls -1 "$tmp" | head -n1)"
if [ ! -f "$src/$SCRIPT" ]; then
echo "Error: $SCRIPT not found in the downloaded archive." >&2
exit 1
fi

# --- Install the script -----------------------------------------------------
mkdir -p "$INSTALL_DIR"
cp "$src/$SCRIPT" "$INSTALL_DIR/$SCRIPT"
chmod +x "$INSTALL_DIR/$SCRIPT"
echo "Installed $INSTALL_DIR/$SCRIPT"

case ":$PATH:" in
*":$INSTALL_DIR:"*) ;;
*)
echo ""
echo "WARNING: $INSTALL_DIR is not in your PATH."
echo "Add this to your shell config (~/.zshrc or ~/.bashrc):"
echo ""
echo " export PATH=\"$INSTALL_DIR:\$PATH\""
echo ""
;;
esac

# --- Install the skills -----------------------------------------------------
if [ "$SKIP_SKILLS" = "1" ]; then
echo "Skipping skills (--no-skills)."
exit 0
fi

if [ ! -d "$src/skills" ]; then
echo "No skills/ directory in the archive; nothing else to do."
exit 0
fi

CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
SKILL_TARGETS="$HOME/.claude/skills $CODEX_HOME/skills $HOME/.gemini/antigravity-cli/skills"

installed_any=0
for dir in "$src"/skills/*/; do
[ -f "$dir/SKILL.md" ] || continue
name="$(basename "$dir")"
for target in $SKILL_TARGETS; do
mkdir -p "$target"
rm -rf "$target/$name"
if command -v rsync >/dev/null 2>&1; then
rsync -a "$dir" "$target/$name/"
else
mkdir -p "$target/$name"
cp -R "$dir." "$target/$name/"
fi
echo " skill $name -> $target/$name"
installed_any=1
done
done

if [ "$installed_any" -eq 0 ]; then
echo "No skills found under skills/<name>/SKILL.md."
else
echo ""
echo "Done. Restart Codex and Antigravity to pick up the skills."
fi
Loading