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
68 changes: 40 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,54 @@
# Ghost

**Ghost helps agents keep generated UI aligned with a project's design language.**
**Ghost turns implemented UI into a repo-local design-language file agents can use.**

Agents can ship UI now. The problem is drift: wrong palette, wrong density, wrong hierarchy, wrong feel. Most repos do not have a clear, local answer to “what should this interface look like?”
Agents can ship UI now. What slips is taste: palette, density, hierarchy, and the small choices that make a product feel like itself. Most repos do not have a clear, local answer to “what should this interface look like?”

Ghost gives the repo that answer. A scan creates three files: `map.md` says where the design system lives, `survey.json` records the values found in source, and `fingerprint.md` is the compact design-language contract agents read for generation and review. Agents do the judgment work; Ghost's CLIs provide deterministic checks, diffs, and comparisons.
Ghost gives the repo that answer. It scans the UI already in the codebase and writes `fingerprint.md`: a readable design-language file that lives beside the code. Agents read it before generating UI and check against it after they make changes.

## BYOA: bring your own agent
The scan earns that file with evidence:

Every Ghost workflow runs in the host agent you already use: Claude Code, Codex, Cursor, Goose, or another agent. Each tool ships an [agentskills.io](https://agentskills.io)-compatible recipe bundle for the interpretive work: profile, review, verify, remediate, or summarize a fleet. The agent reads the files, makes the calls, and writes the outputs. When it needs a reproducible answer, such as schema validation or fingerprint distance, it calls a Ghost CLI.
- **`map.md`** tells Ghost where to look.
- **`survey.json`** records the values, tokens, components, and surfaces it found.
- **`fingerprint.md`** explains what those values mean as a design language.

No API key is required to run any CLI verb. Each tool's `emit skill` verb installs its bundle into your agent.

## The four tools
Your agent does the judgment work. Ghost's CLIs handle the repeatable checks: validation, profile verification, diffs, comparisons, and fleet views.

Ghost is split into one responsibility per tool. A scan produces three artifacts in sequence — `map.md` (map the system) → `survey.json` (survey what exists) → `fingerprint.md` (express what it means) — all owned by `ghost-fingerprint`.

| Tool | Owns | Verbs |
| --- | --- | --- |
| **`ghost-fingerprint`** | the three-stage scan pipeline (`map.md`, `survey.json`, `fingerprint.md`) | `inventory`, `lint`, `verify-profile`, `describe`, `diff`, `survey <op>`, `emit` |
| **`ghost-drift`** | `.ghost/history.jsonl` + `.ghost-sync.json` — drift detection and stance | `compare`, `ack`, `track`, `diverge`, `emit skill` |
| **`ghost-fleet`** | `fleet.md` — read-only view across many `(map.md, fingerprint.md)` members | `members`, `view`, `emit skill` |
| **`ghost-ui`** | A reference design system Ghost dogfoods — 97 shadcn components + an MCP server | (no verbs) |
## Works with your agent

Scans describe one subject, but they can read more than one source. For example, an app can be the `primary` source while an upstream design-system package acts as a `resolver` for imported tokens. That keeps the fingerprint about the app's actual usage instead of treating the upstream inventory as the app's design language.
Every Ghost workflow runs in the host agent you already use: Claude Code, Codex, Cursor, Goose, or another agent. Each tool ships an [agentskills.io](https://agentskills.io)-compatible recipe bundle for work like scan, review, verify, remediate, or summarize a fleet. The agent reads the files, makes the calls, and writes the outputs. When it needs a reproducible answer, such as schema validation or fingerprint distance, it calls a Ghost CLI.

`@ghost/core` underneath is a workspace-only library with embedding math, target resolution, skill-bundle loader, and the `ghost.map/v2` + `ghost.survey/v2` schemas the three CLIs share.
No API key is required to run any CLI verb. Each tool's `emit skill` verb installs its bundle into your agent.

## Why Ghost?

Ghost gives agents a few practical abilities:

- **Generate from a local contract**: `fingerprint.md` tells the agent what the design language is before it writes UI.
- **Generate from a local file**: `fingerprint.md` tells the agent what the design language is before it writes UI.
- **Review changes for drift**: the review and verify recipes compare generated or changed UI against the fingerprint.
- **Compare systems**: `ghost-drift compare` and `ghost-fleet view` show how fingerprints differ across projects.
- **Record intent**: `ack`, `track`, and `diverge` record whether drift is accepted, tracked against a new reference, or intentionally different.
- **Stay readable**: `fingerprint.md` is Markdown with YAML frontmatter. Humans can review it, agents can use it, and the CLIs can validate and diff it.

## Tools around the loop

Ghost is split into focused tools. The common path is simple:

```text
map.md -> survey.json -> fingerprint.md -> design review
```

| Tool | Job | Verbs |
| --- | --- | --- |
| **`ghost-fingerprint`** | Create and check `map.md`, `survey.json`, and `fingerprint.md`. | `inventory`, `lint`, `verify-profile`, `describe`, `diff`, `survey <op>`, `emit` |
| **`ghost-drift`** | Compare fingerprints, review UI drift, and record what changed intentionally. | `compare`, `ack`, `track`, `diverge`, `emit skill` |
| **`ghost-fleet`** | See how many project fingerprints relate. | `members`, `view`, `emit skill` |
| **`ghost-ui`** | Reference design system Ghost dogfoods — 97 shadcn components + an MCP server. | (no verbs) |

Scans describe one subject, but they can read more than one source. For example, an app can read tokens from an upstream design-system package while still producing a fingerprint about the app's actual UI.

`@ghost/core` underneath is a workspace-only library with embedding math, target resolution, skill-bundle loader, and the `ghost.map/v2` + `ghost.survey/v2` schemas the three CLIs share.

## Repo layout

Ghost is a pnpm monorepo. Four tools, one reference design system, one docs site.
Expand Down Expand Up @@ -100,7 +112,7 @@ Once a skill is installed, ask your agent in plain English ("profile this design

### Quick start

**1. Map the repo** (the topology stage — pre-req for survey + profile). Ask your host agent to write `map.md`, then validate:
**1. Map the repo** (the first checkpoint before survey and profile). Ask your host agent to write `map.md`, then validate:

```bash
ghost-fingerprint inventory # raw signals as JSON (the agent reads this to author map.md)
Expand Down Expand Up @@ -146,15 +158,15 @@ ghost-drift track new-tracked.fingerprint.md
ghost-drift diverge typography --reason "Editorial product uses a different type scale"
```

**6. Emit derived artifacts** (these all live in `ghost-fingerprint` now — they read your `fingerprint.md`):
**6. Emit derived outputs** (these all live in `ghost-fingerprint` now — they read your `fingerprint.md`):

```bash
ghost-fingerprint emit review-command # .claude/commands/design-review.md (per-project slash command)
ghost-fingerprint emit context-bundle # ghost-context/ (SKILL.md + fingerprint.md + prompt.md + tokens.css)
ghost-fingerprint emit skill # .claude/skills/ghost-fingerprint (the agentskills.io bundle)
```

**7. Take the fleet elevation** (when you have ≥2 members each with their own `map.md` and `fingerprint.md`):
**7. View a fleet** (when you have ≥2 members each with their own `map.md` and `fingerprint.md`):

```bash
ghost-fleet members ./fleet # list registered members + freshness
Expand All @@ -170,17 +182,17 @@ just dev

## CLI Commands

Verbs are scoped to the tool that owns the artifact. Pure inputs → pure outputs, no API key required.
Commands are grouped by the tool that owns the file. Pure inputs → pure outputs, no API key required.

| Tool | Command | Description |
| --- | --- | --- |
| `ghost-fingerprint` | `inventory` | Emit raw repo signals (manifests, language histogram, registry presence, top-level tree, git remote) as JSON. Feeds the topology recipe. |
| `ghost-fingerprint` | `lint` | Validate artifact shape for `fingerprint.md`, `map.md`, or `survey.json` — auto-detects by extension/content. |
| `ghost-fingerprint` | `inventory` | Emit raw repo signals (manifests, language histogram, registry presence, top-level tree, git remote) as JSON. Feeds the map recipe. |
| `ghost-fingerprint` | `lint` | Validate file shape for `fingerprint.md`, `map.md`, or `survey.json` — auto-detects by extension/content. |
| `ghost-fingerprint` | `verify-profile` | Validate fingerprint-to-survey fidelity after profiling; palette, spacing, typography, radii, and shadow posture must be survey-backed, and promoted checks must be calibrated. |
| `ghost-fingerprint` | `describe` | Print section ranges + token estimates so agents can selectively load. |
| `ghost-fingerprint` | `diff` | Structural prose-level diff between two fingerprints (NOT vector distance — for that, use `ghost-drift compare`). |
| `ghost-fingerprint` | `survey <op>` | Operate on `ghost.survey/v2` files. Ops: `merge`, `fix-ids`, `summarize`, `catalog`. |
| `ghost-fingerprint` | `emit` | Derive an artifact from `fingerprint.md`: `review-command`, `context-bundle`, or `skill`. |
| `ghost-fingerprint` | `emit` | Derive an output from `fingerprint.md`: `review-command`, `context-bundle`, or `skill`. |
| `ghost-drift` | `compare` | Pairwise (N=2) or composite (N≥3) over runtime fingerprint embeddings. `--semantic` / `--temporal` add qualitative enrichment. |
| `ghost-drift` | `ack` | Record stance toward the tracked fingerprint in `.ghost-sync.json`. |
| `ghost-drift` | `track` | Shift the tracked fingerprint. |
Expand All @@ -196,7 +208,7 @@ The interpretive work is done by recipes the agent runs. Install the relevant bu

| Recipe | Bundle | Capability | Triggered by |
| --- | --- | --- | --- |
| `map` | `ghost-fingerprint` | Author the topology card (stage 1) | "map this repo", "write map.md" |
| `map` | `ghost-fingerprint` | Write the repo map (stage 1) | "map this repo", "write map.md" |
| `survey` | `ghost-fingerprint` | Author the survey of values (stage 2) | "survey design values", "extract design tokens" |
| `profile` | `ghost-fingerprint` | Author the design language (stage 3) | "profile this design language", "write fingerprint.md" |
| `review` | `ghost-drift` | Review PR changes for drift | "review this PR for drift" |
Expand All @@ -213,7 +225,7 @@ These are instructions, not code. The agent executes them using its normal tools

### Environment variables

- `OPENAI_API_KEY` / `VOYAGE_API_KEY`: optional, consumed by `computeSemanticEmbedding` (a `@ghost/core` library function) when a host wants enriched runtime embeddings.
- `OPENAI_API_KEY` / `VOYAGE_API_KEY`: optional, consumed by `computeSemanticEmbedding` (a `@ghost/core` library function) when a host wants enriched semantic comparison.
- `GITHUB_TOKEN`: optional, used by `resolveTrackedFingerprint` when fetching a tracked fingerprint from GitHub (avoids rate limits).

Each CLI auto-loads `.env` and `.env.local` from the working directory.
Expand Down
43 changes: 36 additions & 7 deletions apps/docs/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ThemeProvider } from "ghost-ui";
import { Navigate, Route, Routes, useParams } from "react-router";
import { useEffect } from "react";
import { Navigate, Route, Routes, useLocation, useParams } from "react-router";
import DocsIndex from "@/app/docs/page";
import HomePage from "@/app/page";
import GhostDriftLanding from "@/app/tools/drift/page";
import GhostFingerprintLanding from "@/app/tools/fingerprint/page";
import GhostFleetLanding from "@/app/tools/fleet/page";
import GhostMapLanding from "@/app/tools/map/page";
import ToolsIndex from "@/app/tools/page";
import GhostUiLanding from "@/app/tools/ui/page";
import ComponentPage from "@/app/ui/components/[name]/page";
Expand All @@ -22,6 +22,21 @@ function ComponentRedirect() {
return <Navigate to={`/ui/components/${name}`} replace />;
}

function ScrollToHash() {
const { hash, pathname } = useLocation();

useEffect(() => {
if (!hash) return;

const id = decodeURIComponent(hash.slice(1));
requestAnimationFrame(() => {
document.getElementById(id)?.scrollIntoView({ block: "start" });
});
}, [hash, pathname]);

return null;
}

export function App() {
return (
<ThemeProvider
Expand All @@ -30,14 +45,18 @@ export function App() {
enableSystem
disableTransitionOnChange
>
<ScrollToHash />
<Dock />
<main className="relative z-10 min-h-screen">
<Routes>
<Route index element={<HomePage />} />

{/* Tools — five-card index plus per-tool landings */}
{/* Tools — four-card index plus per-tool landings */}
<Route path="tools" element={<ToolsIndex />} />
<Route path="tools/map" element={<GhostMapLanding />} />
<Route
path="tools/map"
element={<Navigate to="/tools/fingerprint" replace />}
/>
<Route
path="tools/fingerprint"
element={<GhostFingerprintLanding />}
Expand All @@ -48,8 +67,14 @@ export function App() {

{/* Cross-tool docs hub */}
<Route path="docs" element={<DocsIndex />} />
<Route
path="docs/workflow"
element={
<Navigate to="/docs/getting-started#how-ghost-works" replace />
}
/>

{/* MDX-authored doc pages (getting-started + cli reference under /docs/*) */}
{/* MDX-authored doc pages under /docs/* */}
{mdxDocsRoutes()}

{/* Design Language (ghost-ui catalogue) */}
Expand All @@ -74,11 +99,15 @@ export function App() {
/>
<Route
path="tools/drift/concepts"
element={<Navigate to="/tools" replace />}
element={
<Navigate to="/docs/getting-started#how-ghost-works" replace />
}
/>
<Route
path="tools/drift/workflow"
element={<Navigate to="/tools" replace />}
element={
<Navigate to="/docs/getting-started#how-ghost-works" replace />
}
/>

{/* Redirects from legacy root /foundations and /components URLs */}
Expand Down
17 changes: 5 additions & 12 deletions apps/docs/src/app/docs/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useStaggerReveal } from "ghost-ui";
import { BookOpen, Orbit, Rocket } from "lucide-react";
import { BookOpen, Rocket } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router";
import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
Expand All @@ -17,23 +17,16 @@ const sections: {
name: "Getting Started",
href: "/docs/getting-started",
description:
"Install the CLIs, write your first map.md and fingerprint.md, and track drift across the org — in under five minutes.",
"Install Ghost, scan a repo, and learn the loop around fingerprint.md.",
icon: <Rocket className="size-8" strokeWidth={1.5} />,
},
{
name: "CLI Reference",
href: "/docs/cli",
description:
"Sixteen verbs across four tools — ghost-map, ghost-fingerprint, ghost-drift, ghost-fleet. Plus the skill recipes the host agent runs.",
"Commands for checks and comparison, plus the skill recipes your agent runs.",
icon: <BookOpen className="size-8" strokeWidth={1.5} />,
},
{
name: "The Workflow",
href: "/tools",
description:
"The five moves: profile, compare, review, evolve, and zoom out to the org fingerprint — with examples for each.",
icon: <Orbit className="size-8" strokeWidth={1.5} />,
},
];

export default function DocsIndex() {
Expand All @@ -48,12 +41,12 @@ export default function DocsIndex() {
<AnimatedPageHeader
kicker="Docs"
title="Documentation"
description="Cross-tool guides for the Ghost toolchain. Per-tool overviews live under /tools — these pages cover the install, CLI surface, and end-to-end workflow shared across all five tools."
description="Start with the simple loop, then reach for the command reference when you need exact flags and outputs."
/>

<div
ref={ref}
className="pb-16 pt-8 overflow-visible grid gap-4 md:grid-cols-3"
className="pb-16 pt-8 overflow-visible grid gap-4 md:grid-cols-2"
>
{sections.map((item) => (
<Link
Expand Down
Loading
Loading