Add skills-only installers
This commit is contained in:
@@ -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.
|
- 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.
|
- 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).
|
- 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.
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -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.
|
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
|
### What you type → what happens
|
||||||
|
|||||||
126
scripts/install/install-skills.ps1
Normal file
126
scripts/install/install-skills.ps1
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
205
scripts/install/install-skills.sh
Normal file
205
scripts/install/install-skills.sh
Normal 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"
|
||||||
@@ -7,5 +7,7 @@ const websitePublicDir = resolve(appRoot, "website", "public");
|
|||||||
mkdirSync(websitePublicDir, { recursive: true });
|
mkdirSync(websitePublicDir, { recursive: true });
|
||||||
cpSync(resolve(appRoot, "scripts", "install", "install.sh"), resolve(websitePublicDir, "install"));
|
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.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");
|
console.log("[feynman] synced website installers");
|
||||||
|
|||||||
205
website/public/install-skills
Normal file
205
website/public/install-skills
Normal 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"
|
||||||
126
website/public/install-skills.ps1
Normal file
126
website/public/install-skills.ps1
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
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
|
## Stable or pinned releases
|
||||||
|
|
||||||
If you want the latest tagged release instead of the rolling `edge` channel:
|
If you want the latest tagged release instead of the rolling `edge` channel:
|
||||||
|
|||||||
@@ -103,6 +103,10 @@ const installCommands = [
|
|||||||
GitHub
|
GitHub
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<img src="/hero.png" class="w-full" alt="Feynman CLI" />
|
<img src="/hero.png" class="w-full" alt="Feynman CLI" />
|
||||||
|
|||||||
Reference in New Issue
Block a user