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
5 changes: 5 additions & 0 deletions .changeset/drift-readme-post-decompose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ghost-drift": patch
---

Rewrite README to reflect the five-tool decomposition: drift now lists five verbs (compare, ack, track, diverge, emit skill) and points users at `ghost-expression` for the moved authoring verbs (lint, describe, diff, emit review-command, emit context-bundle).
144 changes: 94 additions & 50 deletions CLAUDE.md

Large diffs are not rendered by default.

165 changes: 108 additions & 57 deletions README.md

Large diffs are not rendered by default.

37 changes: 22 additions & 15 deletions apps/docs/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { ThemeProvider } from "ghost-ui";
import { Navigate, Route, Routes, useParams } from "react-router";
import DriftEngineIndex from "@/app/docs/page";
import DocsIndex from "@/app/docs/page";
import WorkflowPage from "@/app/docs/workflow/page";
import HomePage from "@/app/page";
import GhostDriftLanding from "@/app/tools/drift/page";
import GhostExpressionLanding from "@/app/tools/expression/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";
import ComponentsIndex from "@/app/ui/components/page";
import ColorsPage from "@/app/ui/foundations/colors/page";
Expand Down Expand Up @@ -31,15 +36,22 @@ export function App() {
<Routes>
<Route index element={<HomePage />} />

{/* Tools */}
{/* Tools — five-card index plus per-tool landings */}
<Route path="tools" element={<ToolsIndex />} />
<Route path="tools/drift" element={<DriftEngineIndex />} />
<Route path="tools/map" element={<GhostMapLanding />} />
<Route path="tools/expression" element={<GhostExpressionLanding />} />
<Route path="tools/drift" element={<GhostDriftLanding />} />
<Route path="tools/drift/workflow" element={<WorkflowPage />} />
<Route path="tools/fleet" element={<GhostFleetLanding />} />
<Route path="tools/ui" element={<GhostUiLanding />} />

{/* MDX-authored doc pages */}
{/* Cross-tool docs hub */}
<Route path="docs" element={<DocsIndex />} />

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

{/* Design Language (ghost-ui catalogue — not linked from home/dock) */}
{/* Design Language (ghost-ui catalogue) */}
<Route path="ui" element={<DesignLanguageIndex />} />
<Route path="ui/foundations" element={<FoundationsIndex />} />
<Route path="ui/foundations/colors" element={<ColorsPage />} />
Expand All @@ -50,19 +62,14 @@ export function App() {
<Route path="ui/components" element={<ComponentsIndex />} />
<Route path="ui/components/:name" element={<ComponentPage />} />

{/* Redirects from old /docs/* URLs */}
<Route path="docs" element={<Navigate to="/tools/drift" replace />} />
<Route
path="docs/getting-started"
element={<Navigate to="/tools/drift/getting-started" replace />}
/>
{/* Redirects from the previous /tools/drift/{getting-started,cli} URLs */}
<Route
path="docs/cli"
element={<Navigate to="/tools/drift/cli" replace />}
path="tools/drift/getting-started"
element={<Navigate to="/docs/getting-started" replace />}
/>
<Route
path="docs/concepts"
element={<Navigate to="/tools/drift/workflow" replace />}
path="tools/drift/cli"
element={<Navigate to="/docs/cli" replace />}
/>
<Route
path="tools/drift/concepts"
Expand Down
28 changes: 14 additions & 14 deletions apps/docs/src/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@ const sections: {
description: string;
icon: ReactNode;
}[] = [
{
name: "Workflow",
href: "/tools/drift/workflow",
description:
"The five moves: profile, compare, review, evolve, and zoom out to the org expression — with examples for each.",
icon: <Orbit className="size-8" strokeWidth={1.5} />,
},
{
name: "Getting Started",
href: "/tools/drift/getting-started",
href: "/docs/getting-started",
description:
"Install the skill bundle, write your first expression.md, and track drift against another expression — in under five minutes.",
"Install the CLIs, write your first map.md and expression.md, and track drift across the org — in under five minutes.",
icon: <Rocket className="size-8" strokeWidth={1.5} />,
},
{
name: "CLI Reference",
href: "/tools/drift/cli",
href: "/docs/cli",
description:
"Seven deterministic primitives — compare, lint, describe, ack, track, diverge, emit. Plus the skill recipes the host agent runs.",
"Sixteen deterministic primitives across four tools — ghost-map, ghost-expression, ghost-drift, ghost-fleet. Plus the skill recipes the host agent runs.",
icon: <BookOpen className="size-8" strokeWidth={1.5} />,
},
{
name: "Drift Workflow",
href: "/tools/drift/workflow",
description:
"The five moves: profile, compare, review, evolve, and zoom out to the org expression — with examples for each.",
icon: <Orbit className="size-8" strokeWidth={1.5} />,
},
];

export default function DocsIndex() {
Expand All @@ -46,9 +46,9 @@ export default function DocsIndex() {
return (
<SectionWrapper>
<AnimatedPageHeader
kicker="Drift"
title="Drift"
description="Ghost profiles design languages into human-readable expressions, tracks their evolution, gates AI-generated UI against them, and surfaces divergence before it compounds."
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."
/>

<div
Expand Down
23 changes: 12 additions & 11 deletions apps/docs/src/app/docs/workflow/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -841,12 +841,13 @@ export default function WorkflowPage() {
<StepLabel>Step 01 · Profile</StepLabel>
<StepTitle>Write an expression.md</StepTitle>
<StepLead>
Open your project in a host agent with the <code>ghost-drift</code>{" "}
skill installed and ask it to <em>profile this design language</em>.
The recipe walks the agent through your theme CSS, tailwind config,
and component primitives, resolves variable chains, and writes a
single <code>expression.md</code> at the repo root — YAML frontmatter
for machines, Markdown body for humans.
Open your project in a host agent with the{" "}
<code>ghost-expression</code> skill installed and ask it to{" "}
<em>profile this design language</em>. The recipe walks the agent
through your theme CSS, tailwind config, and component primitives,
resolves variable chains, and writes a single{" "}
<code>expression.md</code> at the repo root — YAML frontmatter for
machines, Markdown body for humans.
</StepLead>
<ExpressionExcerpt />
<div className="reveal mt-8 grid sm:grid-cols-3 gap-4">
Expand Down Expand Up @@ -886,8 +887,8 @@ export default function WorkflowPage() {
<p className="reveal mt-8 text-sm text-muted-foreground max-w-[52ch] leading-relaxed">
Ghost never calls an LLM itself. The agent writes the expression; the
CLI lints, compares, and diffs it deterministically. The final step of
every profile is <code>ghost-drift lint</code> — which validates the
schema and flags body/frontmatter incoherence before anything else
every profile is <code>ghost-expression lint</code> — which validates
the schema and flags body/frontmatter incoherence before anything else
touches it.
</p>
</Step>
Expand Down Expand Up @@ -958,11 +959,11 @@ export default function WorkflowPage() {
</code>{" "}
for distance,{" "}
<code className="rounded bg-muted px-1.5 py-0.5 text-xs font-mono">
ghost-drift lint
ghost-expression lint
</code>{" "}
for validation. For a per-project, pre-baked review command, run{" "}
<code className="rounded bg-muted px-1.5 py-0.5 text-xs font-mono">
ghost-drift emit review-command
ghost-expression emit review-command
</code>{" "}
and the agent will pick it up the next time you open a PR.
</p>
Expand Down Expand Up @@ -1086,7 +1087,7 @@ export default function WorkflowPage() {
<div className="reveal grid sm:grid-cols-4 gap-4">
{[
{
step: "ghost-drift emit context-bundle",
step: "ghost-expression emit context-bundle",
name: "Ground",
desc: "Write SKILL.md + tokens.css + prompt.md from expression.md. Whatever the generator consumes.",
},
Expand Down
17 changes: 10 additions & 7 deletions apps/docs/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export default function Home() {
driven by a human operator through an agentic workflow; sometimes
it runs fully autonomously. The economics have shifted with it. We
no longer assume humans will sit in every diff; we assume the
harness (the guardrails, the reviewers, the verifiers) catches
drift and course-corrects before it lands.
harness the guardrails, the reviewers, the verifiers catches
divergence before it lands.
</p>
<p className="thesis-item">
In that world, ensuring every generation reflects a brand's voice
Expand Down Expand Up @@ -64,18 +64,21 @@ export default function Home() {
in the repo, versioned with the code, edited in the same PRs as
the features it shapes. And it has to evolve. Brand isn't locked
in at the start; it shifts as the product ships, as taste
sharpens, as new surfaces appear.
sharpens, as new surfaces appear, as the org grows new products
around it.
</p>
<p className="thesis-item">
Which raises the governance question. The reflex is to centralize:
one source of truth, many downstream projects, compliance tracked
from above. Ghost takes the opposite approach. Each repo owns its
expression, its trajectory, and its stance. Decentralization
without intent is entropy, so stances (<em>aligned</em>,{" "}
<em>accepted</em>, <em>diverging</em>) turn drift into signal. The
fleet of expressions drifts in the open; every divergence carries
reasoning. Nothing is prescriptive. Nothing drifts silently.
Everything is transparent.
<em>accepted</em>, <em>diverging</em>) turn divergence into
signal. The fleet of expressions drifts in the open; every
divergence carries reasoning. And read from above, the fleet
becomes a world model — the shape of the org's design language,
drawn from the languages inside it. Nothing is prescriptive.
Nothing drifts silently. Everything is transparent.
</p>
</div>
</section>
Expand Down
81 changes: 81 additions & 0 deletions apps/docs/src/app/tools/drift/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use client";

import { useStaggerReveal } from "ghost-ui";
import { BookOpen, Orbit, Rocket } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router";
import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
import { SectionWrapper } from "@/components/docs/wrappers";

const cards: {
name: string;
href: string;
description: string;
icon: ReactNode;
}[] = [
{
name: "Workflow",
href: "/tools/drift/workflow",
description:
"The five moves: profile, compare, review, evolve, and zoom out to the org expression — with examples for each.",
icon: <Orbit className="size-8" strokeWidth={1.5} />,
},
{
name: "Get started",
href: "/docs/getting-started",
description:
"Install the ghost-drift skill bundle and start tracking drift against a reference expression.",
icon: <Rocket className="size-8" strokeWidth={1.5} />,
},
{
name: "CLI reference",
href: "/docs/cli#ghost-drift--drift-detection--governance",
description:
"compare, ack, track, diverge, and emit skill — plus the review / verify / remediate recipes.",
icon: <BookOpen className="size-8" strokeWidth={1.5} />,
},
];

export default function GhostDriftLanding() {
const ref = useStaggerReveal<HTMLDivElement>(".tool-card", {
stagger: 0.06,
y: 30,
duration: 0.7,
});

return (
<SectionWrapper>
<AnimatedPageHeader
kicker="ghost-drift"
title="Drift"
description="Detect divergence the moment it happens, then record the right stance. compare returns scalar distance and per-dimension deltas across two or many expressions; ack / track / diverge turn unintended drift into intentional signal. The skill bundle ships the review, verify, compare, and remediate recipes the host agent runs."
/>

<div
ref={ref}
className="grid gap-4 md:grid-cols-3 pb-16 overflow-visible"
>
{cards.map((item) => (
<Link
key={item.href}
to={item.href}
className="tool-card group rounded-[var(--radius-card-sm)] border border-border-card hover:border-foreground/25 bg-card p-10 transition-colors duration-300"
>
<div className="mb-6 text-muted-foreground group-hover:text-foreground transition-colors duration-200">
{item.icon}
</div>
<span className="relative inline-block font-display text-lg font-bold tracking-tight">
<span className="relative z-10 transition-colors duration-300 group-hover:text-background">
{item.name}
</span>
<span className="absolute inset-0 bg-foreground origin-left scale-x-0 group-hover:scale-x-100 transition-transform duration-300 ease-out" />
</span>
<p className="mt-2 text-sm text-muted-foreground leading-relaxed">
{item.description}
</p>
</Link>
))}
</div>
</SectionWrapper>
);
}
81 changes: 81 additions & 0 deletions apps/docs/src/app/tools/expression/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use client";

import { useStaggerReveal } from "ghost-ui";
import { BookOpen, FileText, Rocket } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router";
import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
import { SectionWrapper } from "@/components/docs/wrappers";

const cards: {
name: string;
href: string;
description: string;
icon: ReactNode;
}[] = [
{
name: "Get started",
href: "/docs/getting-started",
description:
"Install the ghost-expression skill bundle and ask your agent to profile a design language.",
icon: <Rocket className="size-8" strokeWidth={1.5} />,
},
{
name: "CLI reference",
href: "/docs/cli#ghost-expression--authoring--validation",
description:
"lint, describe, diff, and emit (review-command, context-bundle, skill).",
icon: <BookOpen className="size-8" strokeWidth={1.5} />,
},
{
name: "Format spec",
href: "https://github.com/block/ghost/blob/main/docs/expression-format.md",
description:
"The full expression.md spec — frontmatter schema, three-layer body, 49-dim machine vector.",
icon: <FileText className="size-8" strokeWidth={1.5} />,
},
];

export default function GhostExpressionLanding() {
const ref = useStaggerReveal<HTMLDivElement>(".tool-card", {
stagger: 0.06,
y: 30,
duration: 0.7,
});

return (
<SectionWrapper>
<AnimatedPageHeader
kicker="ghost-expression"
title="Authoring"
description="The package that owns expression.md — Ghost's canonical design-language artifact. YAML frontmatter for machines, three-layer prose body (Character / Signature / Decisions) for humans and LLMs. Validated, described, diffed, and emitted into per-project review commands and grounding bundles for any generator."
/>

<div
ref={ref}
className="grid gap-4 md:grid-cols-3 pb-16 overflow-visible"
>
{cards.map((item) => (
<Link
key={item.href}
to={item.href}
className="tool-card group rounded-[var(--radius-card-sm)] border border-border-card hover:border-foreground/25 bg-card p-10 transition-colors duration-300"
>
<div className="mb-6 text-muted-foreground group-hover:text-foreground transition-colors duration-200">
{item.icon}
</div>
<span className="relative inline-block font-display text-lg font-bold tracking-tight">
<span className="relative z-10 transition-colors duration-300 group-hover:text-background">
{item.name}
</span>
<span className="absolute inset-0 bg-foreground origin-left scale-x-0 group-hover:scale-x-100 transition-transform duration-300 ease-out" />
</span>
<p className="mt-2 text-sm text-muted-foreground leading-relaxed">
{item.description}
</p>
</Link>
))}
</div>
</SectionWrapper>
);
}
Loading
Loading