diff --git a/README.md b/README.md index edf5454..0083b46 100644 --- a/README.md +++ b/README.md @@ -338,15 +338,15 @@ engine cannot prove it, the dashboard cannot show it. | Target API: invoices + ownership (`401/403/404`) | done | | Autopsy engine: thresholds, trend, attribution, heal check | done, exercised live against the fixture | | Metrics chart + diff viewer driven by fixture data | done | -| OpenCode collector SSE wiring | **scaffold** (contract designed) | -| Metrics runtime sampler loop | **scaffold** (contract designed) | -| Shadow-git checkpoints / one-click rollback | **planned** | -| Ghost Cloud (auth, projects, session sync) | **planned** — MVP 2 | +| OpenCode collector SSE wiring | done — Mode A plugin streams real session events (`packages/opencode-plugin`) | +| Metrics runtime sampler loop | done — Mode C probes poll `/api/health` and emit MetricSamples | +| Shadow-git checkpoints / one-click rollback | done — throwaway index + `commit-tree`, never touches HEAD | +| Ghost Cloud (auth, projects, session sync) | done — GitHub OAuth, device pairing, Neon Postgres (`/login` + `/pair`) | -The dashboard today replays the committed fixture — that is the intended -scenario view. The recorder underneath is being wired in the roadmap order -above. Nothing in the UI fakes live behavior: parts that are not wired yet are -not presented as working. +The dashboard replays a recorded session end-to-end. The recorder underneath is +**live**: the Ghost CLI records real agent sessions into a shadow-git store and +syncs them to Ghost Cloud when a device is paired. Nothing in the UI fakes live +behavior: parts that are not wired yet are not presented as working. --- @@ -362,6 +362,35 @@ npm run typecheck # tsc --noEmit Deploy on Vercel with `framework: Next.js` — no environment variables required; ownership in the demo API is enforced in code, not behind a flag. +## 🧰 Install the Ghost CLI — one curl line, no npm + +The recorder is a plain Node script (no packages, no build step). Requires +**Node.js >= 18** and **curl + tar** (both ship with macOS, most Linux distros, +and Windows 10+). + +**macOS / Linux:** + +```bash +curl -fsSL https://raw.githubusercontent.com/arko33/ghostinthecodebase/main/packages/ghost/install.sh | bash +``` + +**Windows (PowerShell):** + +```powershell +curl -fsSL https://raw.githubusercontent.com/arko33/ghostinthecodebase/main/packages/ghost/install.ps1 -o ghost-install.ps1 +powershell -ExecutionPolicy Bypass -File ghost-install.ps1 +``` + +Both installers download the recorder from this repo, place it in `~/.ghost`, +and put a `ghost` command on your PATH. Then, from any project directory: + +```bash +ghost status # repo head, cloud pairing state, sync policy +ghost watch # record: watch the working tree, checkpoint each change burst, sample runtime metrics +ghost sessions # list sessions stored in .ghost +ghost setup # optional: pair the device with Ghost Cloud (metadata-only sync; source code never leaves the machine) +``` + ## 🛠️ Stack
diff --git a/packages/ghost/install.ps1 b/packages/ghost/install.ps1 new file mode 100644 index 0000000..74f792b --- /dev/null +++ b/packages/ghost/install.ps1 @@ -0,0 +1,60 @@ +# Ghost CLI installer — Windows (PowerShell 5.1+; uses built-in curl.exe + tar.exe, no npm) +# curl -fsSL https://raw.githubusercontent.com/arko33/ghostinthecodebase/main/packages/ghost/install.ps1 -o ghost-install.ps1 +# powershell -ExecutionPolicy Bypass -File ghost-install.ps1 +# Result: `ghost` command on user PATH, recorder files in %USERPROFILE%\.ghost\lib +$ErrorActionPreference = "Stop" + +$Repo = "arko33/ghostinthecodebase" +$Ref = if ($env:GHOST_REF) { $env:GHOST_REF } else { "main" } +$GhostLib = Join-Path $HOME ".ghost\lib" +$GhostBin = Join-Path $HOME ".ghost\bin" +$Tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("gh-" + [Guid]::NewGuid().ToString("N")) + +Write-Host "==> Downloading Ghost recorder (branch: $Ref)" +New-Item -ItemType Directory -Force -Path $Tmp, $GhostLib, $GhostBin | Out-Null +$Tgz = Join-Path $Tmp "ghost.tar.gz" +& curl.exe -fsSL "https://codeload.github.com/$Repo/tar.gz/refs/heads/$Ref" -o $Tgz +if ($LASTEXITCODE -ne 0) { throw "download failed (curl exit $LASTEXITCODE)" } + +& tar -xzf $Tgz -C $Tmp +$Src = Get-ChildItem -Path $Tmp -Recurse -Directory -Filter ghost | + Where-Object { $_.FullName -like "*packages\ghost" -or $_.FullName -like "*packages/ghost" } | + Select-Object -First 1 +if (-not $Src) { throw "packages/ghost not found in tarball" } + +Write-Host "==> Installing to $GhostLib" +Copy-Item -Path (Join-Path $Src.FullName "*") -Destination $GhostLib -Recurse -Force + +if (-not (Get-Command node -ErrorAction SilentlyContinue)) { + Write-Host "" + Write-Host "x Node.js >= 18 is required (the CLI is a plain node script)." -ForegroundColor Red + Write-Host " Install it from https://nodejs.org, then re-run this installer." -ForegroundColor Red + exit 1 +} + +$Shim = Join-Path $GhostBin "ghost.cmd" +Set-Content -Path $Shim -Value "@echo off`r`nnode `"$GhostLib\ghost.mjs`" %*" -Encoding ascii +Write-Host "==> Shim written: $Shim" + +# Add to the user PATH if not already present +$UserPath = [Environment]::GetEnvironmentVariable("Path", "User") +if ($UserPath -notlike "*$GhostBin*") { + [Environment]::SetEnvironmentVariable("Path", "$UserPath;$GhostBin", "User") + Write-Host "==> Added to user PATH: $GhostBin" +} + +$env:Path = "$env:Path;$GhostBin" +Write-Host "" +Write-Host "v Ghost recorder installed:" -ForegroundColor Green +Write-Host " files : $GhostLib" +Write-Host " shim : $Shim" +Write-Host "" +Write-Host " Usage (from ANY project directory):" +Write-Host " ghost status # summary of repo + cloud pairing" +Write-Host " ghost watch # record/live session (see README)" +Write-Host " ghost sessions # list recorded sessions" +Write-Host " ghost setup # optionally pair with Ghost Cloud" +Write-Host "" +Write-Host " Open a NEW terminal (PATH refresh), then run: ghost status" + +Remove-Item -Recurse -Force $Tmp -ErrorAction SilentlyContinue diff --git a/packages/ghost/install.sh b/packages/ghost/install.sh new file mode 100644 index 0000000..5aa6720 --- /dev/null +++ b/packages/ghost/install.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# Ghost CLI installer — macOS & Linux (pure curl+tar, no npm) +# curl -fsSL https://raw.githubusercontent.com/arko33/ghostinthecodebase/main/packages/ghost/install.sh | bash +# Result: `ghost` command on PATH, recorder files in ~/.ghost/lib +set -euo pipefail + +REPO="arko33/ghostinthecodebase" +REF="${GHOST_REF:-main}" +GHOST_LIB="$HOME/.ghost/lib" +GHOST_BIN="$HOME/.ghost/bin" +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT + +echo "==> Downloading Ghost recorder (branch: ${REF})" +curl -fsSL "https://codeload.github.com/${REPO}/tar.gz/refs/heads/${REF}" -o "$TMP/ghost.tar.gz" +tar -xzf "$TMP/ghost.tar.gz" -C "$TMP" + +SRC="$(find "$TMP" -maxdepth 2 -type d -path '*packages/ghost')" +if [ -z "$SRC" ]; then + echo "x packages/ghost not found in tarball" >&2 + exit 1 +fi + +echo "==> Installing to ${GHOST_LIB}" +mkdir -p "$GHOST_LIB" "$GHOST_BIN" +cp -r "$SRC/." "$GHOST_LIB/" + +if ! command -v node >/dev/null 2>&1; then + echo "" >&2 + echo "x Node.js >= 18 is required (the CLI is a plain node script)." >&2 + echo " Install it from https://nodejs.org, then re-run this installer." >&2 + exit 1 +fi + +cat > "$GHOST_BIN/ghost" </dev/null; then + printf '\n# ghost-to-path\nexport PATH="%s:$PATH"\n' "$GHOST_BIN" >> "$RC" + echo "==> PATH added to $RC" + else + echo "==> PATH already configured in $RC" + fi +} +add_rc "$HOME/.profile" +if [ -n "${ZSH_VERSION:-}" ] && [ "$HOME/.zshrc" != "$HOME/.profile" ]; then + add_rc "$HOME/.zshrc" +fi + +echo "" +echo "✓ Ghost recorder installed:" +echo " files : ${GHOST_LIB}" +echo " shim : ${GHOST_BIN}/ghost" +echo "" +echo " Usage (from ANY project directory):" +echo " ghost status # summary of repo + cloud pairing" +echo " ghost watch # record/live session View README" +echo " ghost sessions # list recorded sessions" +echo " ghost setup # optionally pair with Ghost Cloud" +echo "" +echo " Open a NEW shell (or: export PATH=\"${GHOST_BIN}:\$PATH\") then run: ghost status" diff --git a/src/components/site/Terminal.tsx b/src/components/site/Terminal.tsx index b6fbbe8..5f9c423 100644 --- a/src/components/site/Terminal.tsx +++ b/src/components/site/Terminal.tsx @@ -5,9 +5,9 @@ import { useEffect, useRef, useState } from "react"; import { useInView, useReducedMotion } from "./hooks"; const COMMANDS = [ - "npm install", - "npm run dev", - "npm run record -- --repo . --health http://localhost:3000/api/health --duration 60", + "curl -fsSL https://raw.githubusercontent.com/arko33/ghostinthecodebase/main/packages/ghost/install.sh | bash", + "ghost watch --repo . --app-url http://localhost:3000", + "ghost status", ]; interface Props {