Add skills-only installers

This commit is contained in:
Advait Paliwal
2026-03-25 14:52:20 -07:00
parent ca74226c83
commit c8536583bf
9 changed files with 725 additions and 0 deletions

View File

@@ -50,3 +50,12 @@ Use this file to track chronology, not release notes. Keep entries short, factua
- Failed / learned: A pooled statistical meta-analysis would be misleading because the literature mixes heterogeneous outcomes, scaling axes, and evaluation regimes; final deliverable uses a qualitative meta-analysis instead.
- Blockers: None for this brief.
- Next: If needed, extend into a narrower sub-survey (e.g. only pretraining laws, only inference-time scaling, or only post-Chinchilla data-quality revisions).
### 2026-03-25 14:52 local — skills-only-install
- Objective: Let users download the Feynman research skills without installing the full terminal runtime.
- Changed: Added standalone skills-only installers at `scripts/install/install-skills.sh` and `scripts/install/install-skills.ps1`; synced website-public copies; documented user-level and repo-local install flows in `README.md`, `website/src/content/docs/getting-started/installation.md`, and `website/src/pages/index.astro`.
- Verified: Ran `sh -n scripts/install/install-skills.sh`; ran `node scripts/sync-website-installers.mjs`; ran `cd website && npm run build`; executed `sh scripts/install/install-skills.sh --dir <tmp>` and confirmed extracted `SKILL.md` files land in the target directory.
- Failed / learned: PowerShell installer behavior was not executed locally because PowerShell is not installed in this environment.
- Blockers: None for the Unix installer flow; Windows remains syntax-only by inspection.
- Next: If users want this exposed more prominently, add a dedicated docs/reference page and a homepage-specific skills-only CTA instead of a text link.

View File

@@ -19,6 +19,24 @@ curl -fsSL https://feynman.is/install | bash
If you install via `pnpm` or `bun` instead of the standalone bundle, Feynman requires Node.js `20.18.1` or newer.
### Skills Only
If you want just the research skills without the full terminal app:
```bash
curl -fsSL https://feynman.is/install-skills | bash
```
That installs the skill library into `~/.codex/skills/feynman`.
For a repo-local install instead:
```bash
curl -fsSL https://feynman.is/install-skills | bash -s -- --repo
```
That installs into `.agents/skills/feynman` under the current repository.
---
### What you type → what happens

View File

@@ -0,0 +1,126 @@
param(
[string]$Version = "edge",
[ValidateSet("User", "Repo")]
[string]$Scope = "User",
[string]$TargetDir = ""
)
$ErrorActionPreference = "Stop"
function Normalize-Version {
param([string]$RequestedVersion)
if (-not $RequestedVersion) {
return "edge"
}
switch ($RequestedVersion.ToLowerInvariant()) {
"edge" { return "edge" }
"latest" { return "latest" }
"stable" { return "latest" }
default { return $RequestedVersion.TrimStart("v") }
}
}
function Resolve-VersionMetadata {
param([string]$RequestedVersion)
$normalizedVersion = Normalize-Version -RequestedVersion $RequestedVersion
if ($normalizedVersion -eq "edge") {
return [PSCustomObject]@{
ResolvedVersion = "edge"
GitRef = "main"
DownloadUrl = "https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.zip"
}
}
if ($normalizedVersion -eq "latest") {
$release = Invoke-RestMethod -Uri "https://api.github.com/repos/getcompanion-ai/feynman/releases/latest"
if (-not $release.tag_name) {
throw "Failed to resolve the latest Feynman release version."
}
$resolvedVersion = $release.tag_name.TrimStart("v")
} else {
$resolvedVersion = $normalizedVersion
}
return [PSCustomObject]@{
ResolvedVersion = $resolvedVersion
GitRef = "v$resolvedVersion"
DownloadUrl = "https://github.com/getcompanion-ai/feynman/archive/refs/tags/v$resolvedVersion.zip"
}
}
function Resolve-InstallDir {
param(
[string]$ResolvedScope,
[string]$ResolvedTargetDir
)
if ($ResolvedTargetDir) {
return $ResolvedTargetDir
}
if ($ResolvedScope -eq "Repo") {
return Join-Path (Get-Location) ".agents\skills\feynman"
}
$codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME ".codex" }
return Join-Path $codexHome "skills\feynman"
}
$metadata = Resolve-VersionMetadata -RequestedVersion $Version
$resolvedVersion = $metadata.ResolvedVersion
$downloadUrl = $metadata.DownloadUrl
$installDir = Resolve-InstallDir -ResolvedScope $Scope -ResolvedTargetDir $TargetDir
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ("feynman-skills-install-" + [System.Guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $tmpDir | Out-Null
try {
$archivePath = Join-Path $tmpDir "feynman-skills.zip"
$extractDir = Join-Path $tmpDir "extract"
Write-Host "==> Downloading Feynman skills $resolvedVersion"
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath
Write-Host "==> Extracting skills"
Expand-Archive -LiteralPath $archivePath -DestinationPath $extractDir -Force
$sourceRoot = Get-ChildItem -Path $extractDir -Directory | Select-Object -First 1
if (-not $sourceRoot) {
throw "Could not find extracted Feynman archive."
}
$skillsSource = Join-Path $sourceRoot.FullName "skills"
if (-not (Test-Path $skillsSource)) {
throw "Could not find skills/ in downloaded archive."
}
$installParent = Split-Path $installDir -Parent
if ($installParent) {
New-Item -ItemType Directory -Path $installParent -Force | Out-Null
}
if (Test-Path $installDir) {
Remove-Item -Recurse -Force $installDir
}
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
Copy-Item -Path (Join-Path $skillsSource "*") -Destination $installDir -Recurse -Force
Write-Host "==> Installed skills to $installDir"
if ($Scope -eq "Repo") {
Write-Host "Repo-local skills will be discovered automatically from .agents/skills."
} else {
Write-Host "User-level skills will be discovered from `$CODEX_HOME/skills."
}
Write-Host "Feynman skills $resolvedVersion installed successfully."
} finally {
if (Test-Path $tmpDir) {
Remove-Item -Recurse -Force $tmpDir
}
}

View File

@@ -0,0 +1,205 @@
#!/bin/sh
set -eu
VERSION="edge"
SCOPE="${FEYNMAN_SKILLS_SCOPE:-user}"
TARGET_DIR="${FEYNMAN_SKILLS_DIR:-}"
step() {
printf '==> %s\n' "$1"
}
normalize_version() {
case "$1" in
"" | edge)
printf 'edge\n'
;;
latest | stable)
printf 'latest\n'
;;
v*)
printf '%s\n' "${1#v}"
;;
*)
printf '%s\n' "$1"
;;
esac
}
download_file() {
url="$1"
output="$2"
if command -v curl >/dev/null 2>&1; then
if [ -t 2 ]; then
curl -fL --progress-bar "$url" -o "$output"
else
curl -fsSL "$url" -o "$output"
fi
return
fi
if command -v wget >/dev/null 2>&1; then
if [ -t 2 ]; then
wget --show-progress -O "$output" "$url"
else
wget -q -O "$output" "$url"
fi
return
fi
echo "curl or wget is required to install Feynman skills." >&2
exit 1
}
download_text() {
url="$1"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$url"
return
fi
if command -v wget >/dev/null 2>&1; then
wget -q -O - "$url"
return
fi
echo "curl or wget is required to install Feynman skills." >&2
exit 1
}
resolve_version() {
normalized_version="$(normalize_version "$VERSION")"
if [ "$normalized_version" = "edge" ]; then
printf 'edge\nmain\n'
return
fi
if [ "$normalized_version" = "latest" ]; then
release_json="$(download_text "https://api.github.com/repos/getcompanion-ai/feynman/releases/latest")"
resolved_version="$(printf '%s\n' "$release_json" | sed -n 's/.*"tag_name":[[:space:]]*"v\([^"]*\)".*/\1/p' | head -n 1)"
if [ -z "$resolved_version" ]; then
echo "Failed to resolve the latest Feynman release version." >&2
exit 1
fi
printf '%s\nv%s\n' "$resolved_version" "$resolved_version"
return
fi
printf '%s\nv%s\n' "$normalized_version" "$normalized_version"
}
resolve_target_dir() {
if [ -n "$TARGET_DIR" ]; then
printf '%s\n' "$TARGET_DIR"
return
fi
case "$SCOPE" in
repo)
printf '%s/.agents/skills/feynman\n' "$PWD"
;;
user)
codex_home="${CODEX_HOME:-$HOME/.codex}"
printf '%s/skills/feynman\n' "$codex_home"
;;
*)
echo "Unknown scope: $SCOPE (expected --user or --repo)" >&2
exit 1
;;
esac
}
while [ $# -gt 0 ]; do
case "$1" in
--repo)
SCOPE="repo"
;;
--user)
SCOPE="user"
;;
--dir)
if [ $# -lt 2 ]; then
echo "Usage: install-skills.sh [edge|stable|latest|<version>] [--user|--repo] [--dir <path>]" >&2
exit 1
fi
TARGET_DIR="$2"
shift
;;
edge|stable|latest|v*|[0-9]*)
VERSION="$1"
;;
*)
echo "Unknown argument: $1" >&2
echo "Usage: install-skills.sh [edge|stable|latest|<version>] [--user|--repo] [--dir <path>]" >&2
exit 1
;;
esac
shift
done
archive_metadata="$(resolve_version)"
resolved_version="$(printf '%s\n' "$archive_metadata" | sed -n '1p')"
git_ref="$(printf '%s\n' "$archive_metadata" | sed -n '2p')"
archive_url=""
case "$git_ref" in
main)
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.tar.gz"
;;
v*)
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/tags/${git_ref}.tar.gz"
;;
esac
if [ -z "$archive_url" ]; then
echo "Could not resolve a download URL for ref: $git_ref" >&2
exit 1
fi
install_dir="$(resolve_target_dir)"
step "Installing Feynman skills ${resolved_version} (${SCOPE})"
tmp_dir="$(mktemp -d)"
cleanup() {
rm -rf "$tmp_dir"
}
trap cleanup EXIT INT TERM
archive_path="$tmp_dir/feynman-skills.tar.gz"
step "Downloading skills archive"
download_file "$archive_url" "$archive_path"
extract_dir="$tmp_dir/extract"
mkdir -p "$extract_dir"
step "Extracting skills"
tar -xzf "$archive_path" -C "$extract_dir"
source_root="$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)"
if [ -z "$source_root" ] || [ ! -d "$source_root/skills" ]; then
echo "Could not find skills/ in downloaded archive." >&2
exit 1
fi
mkdir -p "$(dirname "$install_dir")"
rm -rf "$install_dir"
mkdir -p "$install_dir"
cp -R "$source_root/skills/." "$install_dir/"
step "Installed skills to $install_dir"
case "$SCOPE" in
repo)
step "Repo-local skills will be discovered automatically from .agents/skills"
;;
user)
step "User-level skills will be discovered from \$CODEX_HOME/skills"
;;
esac
printf 'Feynman skills %s installed successfully.\n' "$resolved_version"

View File

@@ -7,5 +7,7 @@ const websitePublicDir = resolve(appRoot, "website", "public");
mkdirSync(websitePublicDir, { recursive: true });
cpSync(resolve(appRoot, "scripts", "install", "install.sh"), resolve(websitePublicDir, "install"));
cpSync(resolve(appRoot, "scripts", "install", "install.ps1"), resolve(websitePublicDir, "install.ps1"));
cpSync(resolve(appRoot, "scripts", "install", "install-skills.sh"), resolve(websitePublicDir, "install-skills"));
cpSync(resolve(appRoot, "scripts", "install", "install-skills.ps1"), resolve(websitePublicDir, "install-skills.ps1"));
console.log("[feynman] synced website installers");

View File

@@ -0,0 +1,205 @@
#!/bin/sh
set -eu
VERSION="edge"
SCOPE="${FEYNMAN_SKILLS_SCOPE:-user}"
TARGET_DIR="${FEYNMAN_SKILLS_DIR:-}"
step() {
printf '==> %s\n' "$1"
}
normalize_version() {
case "$1" in
"" | edge)
printf 'edge\n'
;;
latest | stable)
printf 'latest\n'
;;
v*)
printf '%s\n' "${1#v}"
;;
*)
printf '%s\n' "$1"
;;
esac
}
download_file() {
url="$1"
output="$2"
if command -v curl >/dev/null 2>&1; then
if [ -t 2 ]; then
curl -fL --progress-bar "$url" -o "$output"
else
curl -fsSL "$url" -o "$output"
fi
return
fi
if command -v wget >/dev/null 2>&1; then
if [ -t 2 ]; then
wget --show-progress -O "$output" "$url"
else
wget -q -O "$output" "$url"
fi
return
fi
echo "curl or wget is required to install Feynman skills." >&2
exit 1
}
download_text() {
url="$1"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$url"
return
fi
if command -v wget >/dev/null 2>&1; then
wget -q -O - "$url"
return
fi
echo "curl or wget is required to install Feynman skills." >&2
exit 1
}
resolve_version() {
normalized_version="$(normalize_version "$VERSION")"
if [ "$normalized_version" = "edge" ]; then
printf 'edge\nmain\n'
return
fi
if [ "$normalized_version" = "latest" ]; then
release_json="$(download_text "https://api.github.com/repos/getcompanion-ai/feynman/releases/latest")"
resolved_version="$(printf '%s\n' "$release_json" | sed -n 's/.*"tag_name":[[:space:]]*"v\([^"]*\)".*/\1/p' | head -n 1)"
if [ -z "$resolved_version" ]; then
echo "Failed to resolve the latest Feynman release version." >&2
exit 1
fi
printf '%s\nv%s\n' "$resolved_version" "$resolved_version"
return
fi
printf '%s\nv%s\n' "$normalized_version" "$normalized_version"
}
resolve_target_dir() {
if [ -n "$TARGET_DIR" ]; then
printf '%s\n' "$TARGET_DIR"
return
fi
case "$SCOPE" in
repo)
printf '%s/.agents/skills/feynman\n' "$PWD"
;;
user)
codex_home="${CODEX_HOME:-$HOME/.codex}"
printf '%s/skills/feynman\n' "$codex_home"
;;
*)
echo "Unknown scope: $SCOPE (expected --user or --repo)" >&2
exit 1
;;
esac
}
while [ $# -gt 0 ]; do
case "$1" in
--repo)
SCOPE="repo"
;;
--user)
SCOPE="user"
;;
--dir)
if [ $# -lt 2 ]; then
echo "Usage: install-skills.sh [edge|stable|latest|<version>] [--user|--repo] [--dir <path>]" >&2
exit 1
fi
TARGET_DIR="$2"
shift
;;
edge|stable|latest|v*|[0-9]*)
VERSION="$1"
;;
*)
echo "Unknown argument: $1" >&2
echo "Usage: install-skills.sh [edge|stable|latest|<version>] [--user|--repo] [--dir <path>]" >&2
exit 1
;;
esac
shift
done
archive_metadata="$(resolve_version)"
resolved_version="$(printf '%s\n' "$archive_metadata" | sed -n '1p')"
git_ref="$(printf '%s\n' "$archive_metadata" | sed -n '2p')"
archive_url=""
case "$git_ref" in
main)
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.tar.gz"
;;
v*)
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/tags/${git_ref}.tar.gz"
;;
esac
if [ -z "$archive_url" ]; then
echo "Could not resolve a download URL for ref: $git_ref" >&2
exit 1
fi
install_dir="$(resolve_target_dir)"
step "Installing Feynman skills ${resolved_version} (${SCOPE})"
tmp_dir="$(mktemp -d)"
cleanup() {
rm -rf "$tmp_dir"
}
trap cleanup EXIT INT TERM
archive_path="$tmp_dir/feynman-skills.tar.gz"
step "Downloading skills archive"
download_file "$archive_url" "$archive_path"
extract_dir="$tmp_dir/extract"
mkdir -p "$extract_dir"
step "Extracting skills"
tar -xzf "$archive_path" -C "$extract_dir"
source_root="$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)"
if [ -z "$source_root" ] || [ ! -d "$source_root/skills" ]; then
echo "Could not find skills/ in downloaded archive." >&2
exit 1
fi
mkdir -p "$(dirname "$install_dir")"
rm -rf "$install_dir"
mkdir -p "$install_dir"
cp -R "$source_root/skills/." "$install_dir/"
step "Installed skills to $install_dir"
case "$SCOPE" in
repo)
step "Repo-local skills will be discovered automatically from .agents/skills"
;;
user)
step "User-level skills will be discovered from \$CODEX_HOME/skills"
;;
esac
printf 'Feynman skills %s installed successfully.\n' "$resolved_version"

View File

@@ -0,0 +1,126 @@
param(
[string]$Version = "edge",
[ValidateSet("User", "Repo")]
[string]$Scope = "User",
[string]$TargetDir = ""
)
$ErrorActionPreference = "Stop"
function Normalize-Version {
param([string]$RequestedVersion)
if (-not $RequestedVersion) {
return "edge"
}
switch ($RequestedVersion.ToLowerInvariant()) {
"edge" { return "edge" }
"latest" { return "latest" }
"stable" { return "latest" }
default { return $RequestedVersion.TrimStart("v") }
}
}
function Resolve-VersionMetadata {
param([string]$RequestedVersion)
$normalizedVersion = Normalize-Version -RequestedVersion $RequestedVersion
if ($normalizedVersion -eq "edge") {
return [PSCustomObject]@{
ResolvedVersion = "edge"
GitRef = "main"
DownloadUrl = "https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.zip"
}
}
if ($normalizedVersion -eq "latest") {
$release = Invoke-RestMethod -Uri "https://api.github.com/repos/getcompanion-ai/feynman/releases/latest"
if (-not $release.tag_name) {
throw "Failed to resolve the latest Feynman release version."
}
$resolvedVersion = $release.tag_name.TrimStart("v")
} else {
$resolvedVersion = $normalizedVersion
}
return [PSCustomObject]@{
ResolvedVersion = $resolvedVersion
GitRef = "v$resolvedVersion"
DownloadUrl = "https://github.com/getcompanion-ai/feynman/archive/refs/tags/v$resolvedVersion.zip"
}
}
function Resolve-InstallDir {
param(
[string]$ResolvedScope,
[string]$ResolvedTargetDir
)
if ($ResolvedTargetDir) {
return $ResolvedTargetDir
}
if ($ResolvedScope -eq "Repo") {
return Join-Path (Get-Location) ".agents\skills\feynman"
}
$codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME ".codex" }
return Join-Path $codexHome "skills\feynman"
}
$metadata = Resolve-VersionMetadata -RequestedVersion $Version
$resolvedVersion = $metadata.ResolvedVersion
$downloadUrl = $metadata.DownloadUrl
$installDir = Resolve-InstallDir -ResolvedScope $Scope -ResolvedTargetDir $TargetDir
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ("feynman-skills-install-" + [System.Guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $tmpDir | Out-Null
try {
$archivePath = Join-Path $tmpDir "feynman-skills.zip"
$extractDir = Join-Path $tmpDir "extract"
Write-Host "==> Downloading Feynman skills $resolvedVersion"
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath
Write-Host "==> Extracting skills"
Expand-Archive -LiteralPath $archivePath -DestinationPath $extractDir -Force
$sourceRoot = Get-ChildItem -Path $extractDir -Directory | Select-Object -First 1
if (-not $sourceRoot) {
throw "Could not find extracted Feynman archive."
}
$skillsSource = Join-Path $sourceRoot.FullName "skills"
if (-not (Test-Path $skillsSource)) {
throw "Could not find skills/ in downloaded archive."
}
$installParent = Split-Path $installDir -Parent
if ($installParent) {
New-Item -ItemType Directory -Path $installParent -Force | Out-Null
}
if (Test-Path $installDir) {
Remove-Item -Recurse -Force $installDir
}
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
Copy-Item -Path (Join-Path $skillsSource "*") -Destination $installDir -Recurse -Force
Write-Host "==> Installed skills to $installDir"
if ($Scope -eq "Repo") {
Write-Host "Repo-local skills will be discovered automatically from .agents/skills."
} else {
Write-Host "User-level skills will be discovered from `$CODEX_HOME/skills."
}
Write-Host "Feynman skills $resolvedVersion installed successfully."
} finally {
if (Test-Path $tmpDir) {
Remove-Item -Recurse -Force $tmpDir
}
}

View File

@@ -29,6 +29,36 @@ irm https://feynman.is/install.ps1 | iex
This installs the Windows runtime bundle under `%LOCALAPPDATA%\Programs\feynman`, adds its launcher to your user `PATH`, and lets you re-run the installer at any time to update.
## Skills only
If you only want Feynman's research skills and not the full terminal runtime, install the skill library separately.
For a user-level install into `~/.codex/skills/feynman`:
```bash
curl -fsSL https://feynman.is/install-skills | bash
```
For a repo-local install into `.agents/skills/feynman` under the current repository:
```bash
curl -fsSL https://feynman.is/install-skills | bash -s -- --repo
```
On Windows, install the skills into your Codex skill directory:
```powershell
irm https://feynman.is/install-skills.ps1 | iex
```
Or install them repo-locally:
```powershell
& ([scriptblock]::Create((irm https://feynman.is/install-skills.ps1))) -Scope Repo
```
These installers download only the `skills/` tree from the Feynman repository. They do not install the Feynman terminal, bundled Node runtime, auth storage, or Pi packages.
## Stable or pinned releases
If you want the latest tagged release instead of the rolling `edge` channel:

View File

@@ -103,6 +103,10 @@ const installCommands = [
GitHub
</a>
</div>
<p class="text-sm text-muted-foreground">
Need just the skills? <a href="/docs/getting-started/installation" class="text-primary hover:underline">Install the skills-only bundle</a>.
</p>
</div>
<img src="/hero.png" class="w-full" alt="Feynman CLI" />