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
45 changes: 37 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

---

Expand All @@ -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

<details>
Expand Down
60 changes: 60 additions & 0 deletions packages/ghost/install.ps1
Original file line number Diff line number Diff line change
@@ -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
67 changes: 67 additions & 0 deletions packages/ghost/install.sh
Original file line number Diff line number Diff line change
@@ -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" <<SHIM
#!/usr/bin/env bash
exec node "$GHOST_LIB/ghost.mjs" "\$@"
SHIM
chmod +x "$GHOST_BIN/ghost"

add_rc() {
RC="$1"
[ -f "$RC" ] || touch "$RC"
if ! grep -q "ghost-to-path" "$RC" 2>/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"
6 changes: 3 additions & 3 deletions src/components/site/Terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading