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
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,14 @@ curl -o .opencode/plugins/ghost.ts \

The adapter reports `turn.started` (the prompt admission — the authoritative
turn boundary) and `turn.completed` to the local recorder on
`127.0.0.1:5417/events`. No plugin? Recording still works — the filesystem
watcher + runtime probes capture everything from the outside and turns are
marked **estimated, never invented**.
`127.0.0.1:5417/events`. Note: `ghost watch` **recording** — it does not start
your app. Run your project as usual (e.g. `npm run dev`) in another terminal so
the runtime probes have something to measure.

No plugin? Recording still works — the filesystem watcher + runtime probes
capture everything from the outside and turns are marked **estimated, never
invented**. Not a git repository? File recording still works; run `git init`
to unlock shadow-git checkpoints.

## 🛠️ Stack

Expand Down
2 changes: 2 additions & 0 deletions packages/ghost/ghost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ async function cmdWatch() {
console.log(`\n ● Ghost recorder is live`);
console.log(` session ${session.id}`);
console.log(` repo ${repo}`);
if (!session.head) console.log(` git none — file recording only (run git init to enable shadow-git checkpoints)`);
console.log(` app ${appUrl}`);
console.log(` adapter ${eventsBase}/events (OpenCode plugin target)`);
console.log(` sync ${sync}${sync === "off" ? " (local-only) — run: ghost setup to enable" : ""}`);
Expand Down Expand Up @@ -210,6 +211,7 @@ function cmdStatus() {
const head = shadow.currentHead(ROOT);
console.log(`repo ${ROOT}`);
console.log(`head ${head ? head.slice(0, 10) : "(no git)"}`);
if (!head) console.log(` (not a git repo — run git init to enable shadow-git checkpoints)`);
console.log(`cloud ${config.cloudUrl}${creds ? " (paired)" : " (NOT paired)"}`);
console.log(`sync ${creds ? config.sync : "off (local-only)"}`);
console.log(`settings .ghost/config.json — secrets handled per README policy`);
Expand Down
2 changes: 1 addition & 1 deletion packages/ghost/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ 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 watch # record a live session (README: full guide)"
echo " ghost sessions # list recorded sessions"
echo " ghost setup # optionally pair with Ghost Cloud"
echo ""
Expand Down
4 changes: 2 additions & 2 deletions packages/ghost/lib/shadowgit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function commitSnapshot(repo, message = "ghost checkpoint") {
const env = { ...process.env, GIT_INDEX_FILE: idx };

const git = (args, extraEnv = {}) =>
execFileSync("git", args, { cwd: repo, env: { ...env, ...extraEnv }, encoding: "utf8" }).trim();
execFileSync("git", args, { cwd: repo, env: { ...env, ...extraEnv }, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();

try { git(["read-tree", "HEAD"]); } catch { /* empty repo without HEAD */ }
git(["add", "-A"]);
Expand Down Expand Up @@ -56,6 +56,6 @@ export function showFile(repo, sha, filePath) {
}

export function currentHead(repo) {
try { return execFileSync("git", ["rev-parse", "HEAD"], { cwd: repo, encoding: "utf8" }).trim(); }
try { return execFileSync("git", ["rev-parse", "HEAD"], { cwd: repo, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(); }
catch { return null; }
}
22 changes: 13 additions & 9 deletions src/components/site/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,8 @@ export default function LandingPage() {
</p>
<p style={{ marginTop: 12 }}>
<strong>
13 backend tests. Every pull request. The deployed URL.
23 backend tests + 1 integration flow. Every pull request.
The deployed URL.
</strong>
</p>
</>
Expand Down Expand Up @@ -914,16 +915,19 @@ export default function LandingPage() {
<Badge>Required check</Badge>
</div>
<div className="proof-testsprite__score">
<CountUp target={13} />
<small>/13</small>
<CountUp target={24} />
<small>/24</small>
</div>
<div className="proof-testsprite__state">
<span className="badge__dot" aria-hidden="true" />
Green · suite passes
</div>
<ul className="proof-testsprite__facts">
<li>Runs on every pull request against the deployed URL.</li>
<li>A red suite blocks the merge.</li>
<li>24 tests: 23 backend tests + 1 integration flow.</li>
<li>
The PR check reruns the backend set on every pull request,
against the deployed URL.
</li>
<li>
PR #1 shipped a BOLA hole. PR #2 fixed it. Same suite, green.
</li>
Expand All @@ -935,7 +939,7 @@ export default function LandingPage() {
</span>
<span className="proof__pr" data-state="green">
<GitCommit size={14} weight="light" aria-hidden="true" />
PR #2 · 13/13
PR #2 · fixed · suite green
</span>
</div>
</div>
Expand Down Expand Up @@ -972,12 +976,12 @@ export default function LandingPage() {
{
n: "02",
t: "Connect OpenCode (Mode A)",
d: "Copy the one-file adapter into .opencode/plugins/ghost.ts - OpenCode then reports every turn boundary to the recorder, so the timeline uses real turn ids.",
d: "Two commands (typed by the terminal on the right): create the plugin folder, copy the one-file adapter in. OpenCode then reports every turn boundary to the recorder, so the timeline uses real turn ids.",
},
{
n: "03",
t: "Record any session",
d: "Run ghost watch in your project. With the plugin installed, turns stream live; with any other agent (Cursor, Claude Code, a human) the filesystem watcher + runtime probes record everything from the outside - turns marked estimated, never invented.",
d: "Run ghost watch in your project, and run your app yourself (npm run dev etc.) - the recorder probes its /api/health. With the plugin, turns stream live; with any other agent (Cursor, Claude Code, a human) the filesystem watcher + runtime probes record everything from the outside - turns marked estimated, never invented.",
},
{
n: "04",
Expand Down Expand Up @@ -1084,7 +1088,7 @@ export default function LandingPage() {
</Button>
</div>
<p className="final__note">
<GhostMark size={12} /> Built for TestSprite Season 4 · 13/13
<GhostMark size={12} /> Built for TestSprite Season 4 · 24-test suite
required check
</p>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/site/Terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useInView, useReducedMotion } from "./hooks";

const COMMANDS = [
"curl -fsSL https://raw.githubusercontent.com/arko33/ghostinthecodebase/main/packages/ghost/install.sh | bash",
"mkdir -p .opencode/plugins",
"curl -o .opencode/plugins/ghost.ts https://raw.githubusercontent.com/arko33/ghostinthecodebase/main/packages/opencode-plugin/index.ts",
"ghost watch --repo . --app-url http://localhost:3000",
"ghost status",
Expand Down
Loading