diff --git a/.agents/skills/create-pr/SKILL.md b/.agents/skills/create-pr/SKILL.md
index d1745aea..68941f28 100644
--- a/.agents/skills/create-pr/SKILL.md
+++ b/.agents/skills/create-pr/SKILL.md
@@ -72,14 +72,14 @@ personal or non-Block repositories.
## Step 4: Check Release Hygiene
-Ghost publishes only `@anarchitecture/ghost`; private packages are ignored by
+Ghost publishes only `@design-intelligence/ghost`; private packages are ignored by
Changesets.
Inspect the diff and decide whether a changeset is required:
- Required: user-visible changes to the public package, including CLI behavior,
public exports, schema behavior, package output, docs that affect users, or
- fixes shipped through `@anarchitecture/ghost`.
+ fixes shipped through `@design-intelligence/ghost`.
- Usually not required: tests only, internal scripts only, private package only,
CI-only changes, or docs/ideas notes that are not release-facing.
@@ -87,7 +87,7 @@ When a changeset is required, create one before the PR:
```markdown
---
-"@anarchitecture/ghost": patch
+"@design-intelligence/ghost": patch
---
One sentence, user-facing, present tense.
diff --git a/.changeset/authoring-is-elicitation.md b/.changeset/authoring-is-elicitation.md
new file mode 100644
index 00000000..0740a513
--- /dev/null
+++ b/.changeset/authoring-is-elicitation.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Reframe fingerprint authoring as elicitation in the skill bundle and docs: remove the repo-scan-first auto-draft mode, rebuild the authoring scenarios around what the human says and shows, and point repo-derived reality at the adherence inventory.
diff --git a/.changeset/binding-path-road.md b/.changeset/binding-path-road.md
new file mode 100644
index 00000000..29afd4e5
--- /dev/null
+++ b/.changeset/binding-path-road.md
@@ -0,0 +1,8 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add `ghost.binding/v1` (`.ghost.bind.yml`) and the path road: a repo path
+resolves to the surface that owns it (directory-default binding or explicit
+declaration), and `ghost gather --path ` composes that surface's slice.
+The contract still carries no paths — bindings own all path matching.
diff --git a/.changeset/capture-node-prose-stances.md b/.changeset/capture-node-prose-stances.md
new file mode 100644
index 00000000..03a546bb
--- /dev/null
+++ b/.changeset/capture-node-prose-stances.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Add node prose stances and a scored drafting gate to the capture recipe so authoring agents hold draft nodes to anti-slop standards before human curation.
diff --git a/.changeset/checks-review-on-graph.md b/.changeset/checks-review-on-graph.md
new file mode 100644
index 00000000..2bcdb945
--- /dev/null
+++ b/.changeset/checks-review-on-graph.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+`ghost checks` and `ghost review` now route by catalog references and ground from the selected prose packet: grounding is the surface's gathered nodes by provenance (own / ancestor / edge), replacing the typed why/what (principles, contracts, patterns, exemplars) split — the why and what now live in each node's prose. `ghost checks` gains `--as ` to filter grounding to one output form. Exemplar `path:` is dropped from grounding.
diff --git a/.changeset/collapse-containment-into-folder.md b/.changeset/collapse-containment-into-folder.md
new file mode 100644
index 00000000..c4dd9b12
--- /dev/null
+++ b/.changeset/collapse-containment-into-folder.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Remove redundant containment bookkeeping in favor of path-derived ids: parent and ancestor facts are derived from node paths, and the redundant parent/children maps are removed.
diff --git a/.changeset/collapse-graph-remove-checks-review-migrate.md b/.changeset/collapse-graph-remove-checks-review-migrate.md
new file mode 100644
index 00000000..9a62b325
--- /dev/null
+++ b/.changeset/collapse-graph-remove-checks-review-migrate.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Reduce the fingerprint collection to a flat catalog: remove the `checks`, `review`, and `migrate` commands, drop node `relates` edges and traversal, and reduce `gather` to emitting the menu the agent selects from.
diff --git a/.changeset/composition-graph-gaps.md b/.changeset/composition-graph-gaps.md
new file mode 100644
index 00000000..b669a9fd
--- /dev/null
+++ b/.changeset/composition-graph-gaps.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove check surface routing: every check is now offered to the reviewer and the agent judges relevance, so the check `surface:` field, `selectChecksForSurfaces`, `RoutedCheck`, and `CheckRelevance` are gone. Checks bind to the fingerprint through an optional `source:` pointer (`node > Heading`) that `review` surfaces so a finding can cite the prose it enforces.
diff --git a/.changeset/composition-init-template.md b/.changeset/composition-init-template.md
new file mode 100644
index 00000000..aee966b5
--- /dev/null
+++ b/.changeset/composition-init-template.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+`ghost init --template composition` scaffolds a composition-steering starter: the default files plus an invariants floor (`principle.composition`), a worked bound/open/refines pattern node, a glossary whose `pattern` and `principle` kinds carry the binding-depth convention, and an index that teaches the ladder — patterns when they match, principles as the floor when they don't, narrowing-only between them.
diff --git a/.changeset/config.json b/.changeset/config.json
index 8f5a9933..81dfa838 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -7,5 +7,5 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
- "ignore": ["ghost-ui", "ghost-docs", "ghost-fleet"]
+ "ignore": ["@design-intelligence/vessel", "ghost-docs"]
}
diff --git a/.changeset/consumption-posture.md b/.changeset/consumption-posture.md
new file mode 100644
index 00000000..083aa77d
--- /dev/null
+++ b/.changeset/consumption-posture.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Adds opt-in wild consumption posture for glossary kinds and gather/pull/pulse observability.
diff --git a/.changeset/corridor-gather.md b/.changeset/corridor-gather.md
new file mode 100644
index 00000000..5c7baf48
--- /dev/null
+++ b/.changeset/corridor-gather.md
@@ -0,0 +1,14 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Recompose `gather` on a corridor + hub-and-spoke model and fix a sibling-surface
+context leak. A surface's slice is now: a **spine** of full-body nodes from every
+file on the corridor (the package root down to the surface's own folder — folders
+are walls, so sibling folders never leak in), the **edges** reachable in one hop
+from any spine node's `relates` (so a broad rule authored once high in the tree —
+e.g. `relates: { to: arcade }` on `features/` — reaches every descendant), and a
+set of **spokes**: pointer entries (id + description) for the surface's own
+descendants and any edge hub's subtree, which the agent pulls on demand. The
+`CatalogSlice` JSON gains a `spokes` array; catalog nodes carry their file `folder`.
+Grounding for `checks`/`review` remains the full-body spine + edges.
diff --git a/.changeset/cross-package-extends.md b/.changeset/cross-package-extends.md
new file mode 100644
index 00000000..197850b5
--- /dev/null
+++ b/.changeset/cross-package-extends.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add cross-package inheritance via `extends`. A package's `manifest.yml` can declare `extends: { : }`, mapping another contract's identity to where it lives. Node refs then reference inherited context by identity, never path — `relates: [{ to: brand:core/trust }]` (the `:` form replaces the earlier npm-style `#` ref grammar). Inherited nodes load read-only and flow into gather and validate like local ones. `ghost validate` resolves cross-package refs and reports unresolved refs, packages not declared in `extends`, identity mismatches, and cross-package cycles. This delivers the shared-brand story: one brand contract extended by many products, without copy-paste or merge. One level of `extends` in v1 (no transitive); location is an explicit relative dir (identity-based discovery is a future upgrade that keeps refs unchanged).
diff --git a/.changeset/delete-dormant-entrypoint.md b/.changeset/delete-dormant-entrypoint.md
new file mode 100644
index 00000000..067bb99b
--- /dev/null
+++ b/.changeset/delete-dormant-entrypoint.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Remove the dormant context-selection machinery (`buildContextEntrypoint`, `buildSelectedContext`, and selection-reasons) that was inert since the coordinate removal and orphaned once `review` moved onto the surface rails. Internal cleanup; no public surface change.
diff --git a/.changeset/described-nodes.md b/.changeset/described-nodes.md
new file mode 100644
index 00000000..6208e719
--- /dev/null
+++ b/.changeset/described-nodes.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Make `description` a first-class node field — the retrieval payload an agent matches a task against, the way a tool is selected by name + description. `ghost gather` with no argument now lists nodes by id + description (the catalog), built from the catalog rather than a separate surface menu. Node frontmatter is now passthrough: free-form descriptive keys (`audience`, `stage`, …) are allowed and ride along untouched. The surface composition-edge vocabulary (`composes`/`governed-by`) is removed — lateral composition lives on node `relates`.
diff --git a/.changeset/directory-tree-nodes.md b/.changeset/directory-tree-nodes.md
new file mode 100644
index 00000000..b5263da1
--- /dev/null
+++ b/.changeset/directory-tree-nodes.md
@@ -0,0 +1,18 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Collapse the on-disk node model into the directory tree: the layout supplies the
+catalog. A node's id is its file path (`marketing/email.md` → `marketing/email`)
+and its parent is its containing directory; a surface is just a directory, and a
+directory's own prose lives in its `index.md` (the package-root `index.md` is
+the implicit `core` node). The `surfaces.yml` spine file and the `nodes/`
+directory are removed, along with the node frontmatter `id` and `under` fields —
+identity and containment now come from where a file sits, never from frontmatter
+or a declared spine. Node frontmatter carries descriptive properties only
+(`description`, `relates`, `incarnation`, plus passthrough keys); `relates` and
+cross-package `extends` refs are path ids (`core/trust`, `brand:core/trust`).
+`ghost init` scaffolds `manifest.yml` + a core `index.md`; `ghost migrate`
+writes a directory tree; any `*.md` outside the reserved `checks/` subtree lints
+as a node. Moving a node is a rename — `ghost validate` reports `relates` that no
+longer resolve.
diff --git a/.changeset/docs-check-binding.md b/.changeset/docs-check-binding.md
new file mode 100644
index 00000000..14b9b770
--- /dev/null
+++ b/.changeset/docs-check-binding.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Align docs and the skill bundle with the prose-bound check model: checks are no
+longer routed by surface, every check is offered and the agent judges which
+apply, and a check binds to prose via its optional `source:` pointer. Fix
+the `ghost scan` next-step hint and skill recipes that referenced a nonexistent
+`ghost check` command.
diff --git a/.changeset/docs-node-model-refresh.md b/.changeset/docs-node-model-refresh.md
new file mode 100644
index 00000000..258558c7
--- /dev/null
+++ b/.changeset/docs-node-model-refresh.md
@@ -0,0 +1,6 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Refresh the README and docs site onto the current command set (drop the removed
+`lint`/`verify`/`relay`/`describe`/`survey`/`emit` commands).
diff --git a/.changeset/drop-dead-deps.md b/.changeset/drop-dead-deps.md
new file mode 100644
index 00000000..7cff1570
--- /dev/null
+++ b/.changeset/drop-dead-deps.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Drop two unused runtime dependencies (`jiti`, `tinyglobby`) — neither was imported anywhere in source. Ghost now ships three runtime deps (`cac`, `yaml`, `zod`), shrinking the install footprint by ~1.8 MB. Also fix the build to clear `tsconfig.tsbuildinfo` so `dist/` no longer retains deleted modules from incremental builds (the packed package drops from ~1.9 MB / 777 files to ~397 KB / 248 files).
diff --git a/.changeset/events-tape-first-write-notice.md b/.changeset/events-tape-first-write-notice.md
new file mode 100644
index 00000000..69c7390c
--- /dev/null
+++ b/.changeset/events-tape-first-write-notice.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Print a one-time stderr notice when the local `.ghost/.events` tape is first created, so gather/pull logging is never a surprise.
diff --git a/.changeset/exit-code-contract.md b/.changeset/exit-code-contract.md
new file mode 100644
index 00000000..fea7b82d
--- /dev/null
+++ b/.changeset/exit-code-contract.md
@@ -0,0 +1,8 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Make CLI exit codes consistent so an agent can branch on them: unexpected
+errors exit `1`, caller mistakes (bad flags, invalid environment, refused
+overwrites) exit `2` via a typed `UsageError`, and a missing package now exits
+`2` with a `ghost init` hint instead of leaking a raw filesystem error.
diff --git a/.changeset/export-source-ref-parser.md b/.changeset/export-source-ref-parser.md
new file mode 100644
index 00000000..d0aef29e
--- /dev/null
+++ b/.changeset/export-source-ref-parser.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Export `parseSourceRef` and `sliceNodeSection` from the core entrypoint: the shared parser for a check's `source:` reference grammar (`node-id > Heading`) and a helper to slice the referenced section out of a node body.
diff --git a/.changeset/external-contract-references.md b/.changeset/external-contract-references.md
new file mode 100644
index 00000000..93c50ac0
--- /dev/null
+++ b/.changeset/external-contract-references.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Bindings can reference an external contract: a `.ghost.bind.yml` `contract:` now
+accepts an npm package name (`@scope/brand`) in addition to `.` (in-repo),
+resolved from `node_modules`. `ghost verify` checks the external contract
+resolves and that each bound surface exists in it. External fingerprint loading
+for grounding remains a follow-up.
diff --git a/.changeset/facet-removal.md b/.changeset/facet-removal.md
new file mode 100644
index 00000000..2447a910
--- /dev/null
+++ b/.changeset/facet-removal.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove the facet model — the catalog is now the only fingerprint model. The `intent.yml`/`inventory.yml`/`composition.yml` schemas, the `GhostFingerprintDocument`, the facet→node load-time projection, and the dormant facet slice/grounding are deleted; the loader assembles the package's prose nodes directly into the catalog. `ghost lint` and `ghost verify` are replaced by one `ghost validate` verb (artifact shape pass + catalog pass); `ghost emit` is removed. `ghost scan` now reports node/surface contribution instead of facet contribution. Legacy facet packages no longer load directly — `ghost validate`/load fail with guidance to run `ghost migrate`. Structured exemplar-path and evidence verification is dropped (evidence lives in node prose, per the prose-node model).
diff --git a/.changeset/fingerprint-grounding.md b/.changeset/fingerprint-grounding.md
new file mode 100644
index 00000000..c283036f
--- /dev/null
+++ b/.changeset/fingerprint-grounding.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add fingerprint grounding to `ghost checks`: for each touched surface, emit the
+*why* (principles and experience contracts) and the *what good looks like*
+(patterns and exemplars with paths), drawn from that surface's slice and
+inherited from its ancestors. A flagged check can now cite the design intent it
+serves and point at an exemplar. Use `--no-grounding` for relevance only.
diff --git a/.changeset/first-node-onramp.md b/.changeset/first-node-onramp.md
new file mode 100644
index 00000000..bf993a5d
--- /dev/null
+++ b/.changeset/first-node-onramp.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Lead fingerprint authoring with a concrete first move — capture the one surface whose review feedback keeps repeating — instead of a scenario-classification step.
diff --git a/.changeset/gather-command.md b/.changeset/gather-command.md
new file mode 100644
index 00000000..14c4af2c
--- /dev/null
+++ b/.changeset/gather-command.md
@@ -0,0 +1,7 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add `ghost gather `: compose a surface's context slice (its own placed
+nodes, cascaded ancestors, and one-hop typed-edge contributions) with
+provenance, or return the surface menu when no surface is named.
diff --git a/.changeset/gather-kind-legend.md b/.changeset/gather-kind-legend.md
new file mode 100644
index 00000000..86091b0f
--- /dev/null
+++ b/.changeset/gather-kind-legend.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+`ghost gather` now surfaces the glossary's kind purposes as a menu legend, in both markdown and JSON output, so the menu is self-sufficient selection context.
diff --git a/.changeset/gather-on-graph-incarnation.md b/.changeset/gather-on-graph-incarnation.md
new file mode 100644
index 00000000..cd2dae1f
--- /dev/null
+++ b/.changeset/gather-on-graph-incarnation.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+`ghost gather` now composes its context packet from selected fingerprint catalog nodes and emits nodes-by-provenance prose (own / ancestor / edge), and gains `--as ` to filter the packet to one output form (e.g. email, billboard, voice) while always keeping essence (untagged) nodes.
diff --git a/.changeset/gather-ranking-and-surface-guard.md b/.changeset/gather-ranking-and-surface-guard.md
new file mode 100644
index 00000000..7702e8da
--- /dev/null
+++ b/.changeset/gather-ranking-and-surface-guard.md
@@ -0,0 +1,10 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Return the node menu plus closest-id "did you mean" suggestions when `ghost
+gather` is given an inexact query, instead of a separate ranked-candidate
+search — the agent re-picks by description from the same menu the no-argument
+form prints. `gather`, `checks`, and `review` all emit the stable
+`ERR_UNKNOWN_SURFACE` code with closest-id suggestions for a node or surface
+that is not in the package.
diff --git a/.changeset/gather-vocab.md b/.changeset/gather-vocab.md
new file mode 100644
index 00000000..f79b9485
--- /dev/null
+++ b/.changeset/gather-vocab.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Rename the `gather` slice vocabulary to plain language. The JSON `slice.spokes`
+field is now `slice.pointers`, and the pointer `kind` `"edge-hub"` is now
+`"related"` with its origin in a `from` field (was `hub`). Docs and the skill
+bundle drop the spine/corridor/hub-and-spoke metaphors; `edge` provenance is
+unchanged.
diff --git a/.changeset/ghost-check-format.md b/.changeset/ghost-check-format.md
new file mode 100644
index 00000000..18df0fa2
--- /dev/null
+++ b/.changeset/ghost-check-format.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add `ghost.check/v1`: markdown + frontmatter checks (`name`, `description`,
+`severity`, optional `tools` / `turn-limit`, plus a Ghost `surface:` placement),
+parsed and linted but never executed by Ghost. Markdown files under a `checks/`
+directory lint as checks. This mirrors the established agent-check format so
+Ghost can route and ground checks without owning a check engine.
diff --git a/.changeset/ghost-local-pulse.md b/.changeset/ghost-local-pulse.md
new file mode 100644
index 00000000..1042c5ef
--- /dev/null
+++ b/.changeset/ghost-local-pulse.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add local gather/pull event tracing and a pulse report for fingerprint tuning.
diff --git a/.changeset/goose-skill-install-target.md b/.changeset/goose-skill-install-target.md
new file mode 100644
index 00000000..2055be3f
--- /dev/null
+++ b/.changeset/goose-skill-install-target.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add `goose` as a first-class `ghost skill install --agent` destination, installing to `~/.agents/skills` (Goose's canonical global skills directory) and auto-detecting Goose installs.
diff --git a/.changeset/inventory-lens-reference.md b/.changeset/inventory-lens-reference.md
new file mode 100644
index 00000000..112536fa
--- /dev/null
+++ b/.changeset/inventory-lens-reference.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Add an inventory-lens authoring reference to the skill bundle.
diff --git a/.changeset/materials-transport.md b/.changeset/materials-transport.md
new file mode 100644
index 00000000..60f4f610
--- /dev/null
+++ b/.changeset/materials-transport.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Ghost validates material locator liveness and inlines small local materials during pull.
diff --git a/.changeset/migrate-command.md b/.changeset/migrate-command.md
new file mode 100644
index 00000000..a7a6c1a7
--- /dev/null
+++ b/.changeset/migrate-command.md
@@ -0,0 +1,8 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add `ghost migrate`: transform a legacy `.ghost/` package onto the directory-tree
+node model — derive surface directories from old `topology.scopes`, place
+single-scope nodes inside them, and report (never guess) any node it cannot place
+unambiguously.
diff --git a/.changeset/node-authoring.md b/.changeset/node-authoring.md
new file mode 100644
index 00000000..2368ac24
--- /dev/null
+++ b/.changeset/node-authoring.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+`ghost init` now scaffolds a node package (`manifest.yml` + a core `index.md` node) via a template registry (`--template `, `default` for now) instead of emitting `intent.yml`/`inventory.yml`/`composition.yml`; the `--reference` flag is removed. `ghost migrate` now performs a one-way conversion of legacy/facet packages into a directory tree of nodes (the facet→node projection becomes the writer) and removes the old facet files. The authoring skill (`capture.md`, `SKILL.md`) teaches node authoring with intent/inventory/composition as authoring lenses rather than facet files.
diff --git a/.changeset/one-road-remove-binding-and-nesting.md b/.changeset/one-road-remove-binding-and-nesting.md
new file mode 100644
index 00000000..6204f2d2
--- /dev/null
+++ b/.changeset/one-road-remove-binding-and-nesting.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove the path→surface binding (`ghost.binding/v1`, `.ghost.bind.yml`) and all nesting (fingerprint stacks, cross-package discovery): one contract per package, surfaces are the only locality. `checks` and `review` now take agent-stated `--surface ` instead of resolving surfaces from a diff; `gather` takes only a surface or returns the menu. Removed `gather --path`, `checks --diff`, `lint --all`, `verify --all`, `scan --include-nested`, `emit --path`, `init --scope`, and `init --monorepo`. The agent names the touched surfaces; Ghost no longer infers intent from repo location.
diff --git a/.changeset/pull-command.md b/.changeset/pull-command.md
new file mode 100644
index 00000000..ae08c8e9
--- /dev/null
+++ b/.changeset/pull-command.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add `ghost pull […]`: emit selected nodes' prose bodies and record selections in the `.ghost/.events` events tape for fingerprint tuning.
diff --git a/.changeset/quiet-owls-export.md b/.changeset/quiet-owls-export.md
new file mode 100644
index 00000000..6abc0021
--- /dev/null
+++ b/.changeset/quiet-owls-export.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Adds `ghost export` to package fingerprints as portable brand artifacts with material locator audits.
diff --git a/.changeset/readme-flat-model.md b/.changeset/readme-flat-model.md
new file mode 100644
index 00000000..913cd4e4
--- /dev/null
+++ b/.changeset/readme-flat-model.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Rewrite the package README onto the flat-corpus model: plain-language framing, current CLI commands only, and removal of stale relation-era shapes (`relates`, surface directories, `checks/` at the fingerprint root, `ghost checks`/`ghost review`/`ghost migrate`).
diff --git a/.changeset/remove-compare-drift-fleet.md b/.changeset/remove-compare-drift-fleet.md
new file mode 100644
index 00000000..c22dc87a
--- /dev/null
+++ b/.changeset/remove-compare-drift-fleet.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove `compare`, `drift`, `ack`, `track`, and `diverge` commands and the direct `fingerprint.md` machinery (parser, writer, semantic diff, decisions/dimensions, embeddings, perceptual prior). These rested on a quantified visual-design-system model (fixed dimensions + decision embeddings) that the catalog reframe abandoned; the concepts are parked for a later rethink (see docs/ideas/compare-drift-fleet-rethink.md). The `./compare` and `./drift` package subpaths and the root `compare`/`drift` exports are removed. `ghost lint` now validates `.ghost/` packages and node/surface/check artifacts only (direct `fingerprint.md` is no longer linted); a `*.md` node file lints as a `ghost.node/v1` node.
diff --git a/.changeset/remove-extends.md b/.changeset/remove-extends.md
new file mode 100644
index 00000000..e12668ef
--- /dev/null
+++ b/.changeset/remove-extends.md
@@ -0,0 +1,10 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove cross-package `extends` and inherited nodes. A Ghost package is a single
+self-contained contract: its `.ghost/` directory tree is the whole fingerprint.
+The manifest no longer accepts `extends`, `relates` targets must be local path
+ids (the `:` colon ref is gone), and the catalog drops the
+`origin` / inherited-node distinction. Use `ghost --package ` to address a
+package; there is no shared-brand inheritance in this version.
diff --git a/.changeset/remove-haunt-terminology.md b/.changeset/remove-haunt-terminology.md
new file mode 100644
index 00000000..80145a9a
--- /dev/null
+++ b/.changeset/remove-haunt-terminology.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Removes the optional-capability subsystem and its per-capability manifest schema. Checks are now a core capability in a flat `.ghost/checks/` directory: scaffold with `ghost checks init` (or `ghost init --with checks`), and `ghost export --no-checks` replaces the old exclusion flag. `ghost validate` flags packages still using the old nested checks location.
diff --git a/.changeset/remove-incarnation.md b/.changeset/remove-incarnation.md
new file mode 100644
index 00000000..228cc2f8
--- /dev/null
+++ b/.changeset/remove-incarnation.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove the `incarnation` node field and the `--as` gather/checks flag. Ghost
+fingerprints a single-medium product surface; the cross-medium projection axis
+(essence vs. incarnation, the portability dial) is dropped rather than shipped
+unused. Slices no longer carry an `incarnation` field and `ResolveCatalogSliceOptions`
+is removed.
diff --git a/.changeset/remove-relay-and-legacy-commands.md b/.changeset/remove-relay-and-legacy-commands.md
new file mode 100644
index 00000000..42644584
--- /dev/null
+++ b/.changeset/remove-relay-and-legacy-commands.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove the absorbed and dead commands: `relay`, `stack`, `survey`, `diff`, and
+`describe`, along with the relay-only context modules and the `./relay` package
+export. Their intent now lives in the surface model — `gather` for context,
+`checks` for diff-routed governance, and bindings for path resolution. The skill
+bundle teaches the surface workflow.
diff --git a/.changeset/remove-scan-signals-inventory.md b/.changeset/remove-scan-signals-inventory.md
new file mode 100644
index 00000000..d71270da
--- /dev/null
+++ b/.changeset/remove-scan-signals-inventory.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Remove the `ghost scan` and `ghost signals` commands and the deterministic repo-inventory subsystem; the host agent does its own repo reconnaissance and `validate`/`gather` cover package inspection.
diff --git a/.changeset/remove-validate-one-check-format.md b/.changeset/remove-validate-one-check-format.md
new file mode 100644
index 00000000..844afacc
--- /dev/null
+++ b/.changeset/remove-validate-one-check-format.md
@@ -0,0 +1,9 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Collapse to one check format. Remove `ghost.validate/v1`, the `validate.yml`
+facet, the `ghost check` deterministic gate, and the `./govern` export. Ghost
+now has a single check format — markdown `ghost.check/v1`, routed by surface
+(`ghost checks`) and grounded by the fingerprint. `parseUnifiedDiff` moved to a
+neutral module; the `drift` stance ledger is unchanged.
diff --git a/.changeset/rename-graph-to-catalog.md b/.changeset/rename-graph-to-catalog.md
new file mode 100644
index 00000000..ac554969
--- /dev/null
+++ b/.changeset/rename-graph-to-catalog.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Standardize the internal collection vocabulary on "catalog" to reflect the flat node set: public exports are `GhostCatalog`, `GhostCatalogNode`, `buildCatalogMenu`, `CatalogMenuEntry`, and `assembleCatalog`, and the loaded package exposes a `catalog` field.
diff --git a/.changeset/rename-scope-decentralized-design.md b/.changeset/rename-scope-decentralized-design.md
new file mode 100644
index 00000000..8b8793db
--- /dev/null
+++ b/.changeset/rename-scope-decentralized-design.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Rename the public package from `@anarchitecture/ghost-fingerprint` to `@design-intelligence/ghost`. The `ghost` and `ghost-fingerprint` bins, all subpath exports (`/core`, `/fingerprint`, `/scan`, `/cli`), and the `.ghost/` package format are unchanged; only the npm name moves. Release tags and tarballs now use the `design-intelligence-ghost` prefix.
diff --git a/.changeset/rename-scope-design-intelligence.md b/.changeset/rename-scope-design-intelligence.md
new file mode 100644
index 00000000..fddb65f9
--- /dev/null
+++ b/.changeset/rename-scope-design-intelligence.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Rename the npm scope from `@decentralized-design` to `@design-intelligence`; the package is now `@design-intelligence/ghost` and release tarballs/tags use the `design-intelligence-ghost` prefix.
diff --git a/.changeset/rename-to-ghost-fingerprint.md b/.changeset/rename-to-ghost-fingerprint.md
new file mode 100644
index 00000000..5996d377
--- /dev/null
+++ b/.changeset/rename-to-ghost-fingerprint.md
@@ -0,0 +1,13 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Rename the published package from `@design-intelligence/ghost` to
+`@design-intelligence/ghost`. Ghost is now a family of packages —
+`ghost-fingerprint` (the fingerprint and its CLI), `ghost-adherence` (the
+code-anchored adherence bridge), and `ghost-vessel` (the reference body) —
+and the package name now says which part it is. The `ghost` bin, all export
+subpaths (`/fingerprint`, `/scan`, `/core`, `/cli`), and the `.ghost/`
+on-disk format are unchanged; only the install name moves. Existing installs
+keep working from the old name until it is deprecated on npm with a pointer
+forward.
diff --git a/.changeset/retire-haunt-materials-review.md b/.changeset/retire-haunt-materials-review.md
new file mode 100644
index 00000000..7751112d
--- /dev/null
+++ b/.changeset/retire-haunt-materials-review.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Collapse the separate adherence package into the fingerprint: nodes carry optional `materials` locators, checks become an opt-in capability of the fingerprint package, and `ghost review` assembles advisory diff packets from material-backed nodes and checks. The external dispatch to a second binary is removed.
diff --git a/.changeset/retire-lens-vocabulary.md b/.changeset/retire-lens-vocabulary.md
new file mode 100644
index 00000000..625f9303
--- /dev/null
+++ b/.changeset/retire-lens-vocabulary.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Retire the "three lenses" vocabulary: authoring docs now speak the same steering dimensions the CLI scores (stance, concreteness, patterns), and the inventory recipe becomes references/blocks.md with a neutral block kind in its examples.
diff --git a/.changeset/retire-merge.md b/.changeset/retire-merge.md
new file mode 100644
index 00000000..e8b36790
--- /dev/null
+++ b/.changeset/retire-merge.md
@@ -0,0 +1,10 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Retire the `child-wins-by-id` fingerprint merge (Leak E): nested `.ghost/`
+packages now bind paths to the root contract's surfaces instead of merging their
+own facets in. A path resolves to the single root contract, used as-is — a child
+package can no longer silently override or disable an inherited rule or check.
+The `stack` / `check` / `review` outputs expose `contract` instead of `merged`,
+and drop the `provenance.merge` field.
diff --git a/.changeset/review-on-surfaces.md b/.changeset/review-on-surfaces.md
new file mode 100644
index 00000000..8dd6b614
--- /dev/null
+++ b/.changeset/review-on-surfaces.md
@@ -0,0 +1,10 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Rebuild `ghost review` on the surface rails: it now resolves the diff's touched
+surfaces (via bindings), selects the markdown checks governing them, and grounds
+each in the fingerprint slice — instead of emitting `validate.yml` and a
+path-selection context packet. The advisory-review JSON replaces
+`fingerprint` / `context_markdown` / `checks` / `stacks` with `touched_surfaces`,
+`routed_checks`, and `grounding`. `ghost check` remains the deterministic gate.
diff --git a/.changeset/scope-manifesto.md b/.changeset/scope-manifesto.md
new file mode 100644
index 00000000..231b6d47
--- /dev/null
+++ b/.changeset/scope-manifesto.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Add a thesis section to the README: agents changed the unit of design work, the work that compounds is architectural, and Ghost is the artifact those decisions live in.
diff --git a/.changeset/shared-cli-manifest-builder.md b/.changeset/shared-cli-manifest-builder.md
new file mode 100644
index 00000000..bccd2870
--- /dev/null
+++ b/.changeset/shared-cli-manifest-builder.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add a `ghost manifest --format json` command that emits a self-describing manifest of every command and flag, so a host agent can discover the CLI in one call instead of scraping `--help`. The terminal help, docs-site manifest, and this command all derive from one shared `buildCliManifest()` (also exported from `@design-intelligence/ghost/cli`).
diff --git a/.changeset/skill-bundle-prose.md b/.changeset/skill-bundle-prose.md
new file mode 100644
index 00000000..d41f1e61
--- /dev/null
+++ b/.changeset/skill-bundle-prose.md
@@ -0,0 +1,6 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Clean em dashes out of the shipped skill bundle and package README prose,
+rewriting them as plain sentences, colons, or parentheticals.
diff --git a/.changeset/skill-encoding-guidance.md b/.changeset/skill-encoding-guidance.md
new file mode 100644
index 00000000..765f1877
--- /dev/null
+++ b/.changeset/skill-encoding-guidance.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Make the glossary the authoritative source of kind semantics in the skill recipes, teach sample and counter-exemplar capture, add an `anti-goal` starter kind, and establish the `index` node as the always-pulled carrier of a fingerprint's non-negotiables and silence posture.
diff --git a/.changeset/skill-verb-first.md b/.changeset/skill-verb-first.md
new file mode 100644
index 00000000..80c4ae52
--- /dev/null
+++ b/.changeset/skill-verb-first.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Reorder the skill bundle to lead with the workflow loop and CLI verbs, moving the node-catalog model into a later "How It Works" section.
diff --git a/.changeset/steering-default-template.md b/.changeset/steering-default-template.md
new file mode 100644
index 00000000..2df11c9c
--- /dev/null
+++ b/.changeset/steering-default-template.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Make the steering starter the default `ghost init` scaffold, move the old small scaffold to `--template minimal`, and mark starter steering content as demo guidance to replace.
diff --git a/.changeset/steering-physics-packet.md b/.changeset/steering-physics-packet.md
new file mode 100644
index 00000000..bb366be1
--- /dev/null
+++ b/.changeset/steering-physics-packet.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Adds steering-ordered pulls, derived concreteness coverage, guard posture review routing, check probes, inspect-pointers for binary materials, and a worked steering starter.
diff --git a/.changeset/steering-template.md b/.changeset/steering-template.md
new file mode 100644
index 00000000..f53d7e2e
--- /dev/null
+++ b/.changeset/steering-template.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Adds a steering-ready fingerprint template and skill guidance for authoring, briefing, and auditing agent-readable brand steering.
diff --git a/.changeset/surface-coordinate-space.md b/.changeset/surface-coordinate-space.md
new file mode 100644
index 00000000..3a80b303
--- /dev/null
+++ b/.changeset/surface-coordinate-space.md
@@ -0,0 +1,8 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Replace topology/applies_to/surface_type/scope coordinates with a surface
+coordinate space and a single surface placement per node. Remove the
+`ghost.map/v1` (`map.md`) coordinate and routing system; checks now route by
+`applies_to.paths`.
diff --git a/.changeset/surface-routed-checks.md b/.changeset/surface-routed-checks.md
new file mode 100644
index 00000000..396498fa
--- /dev/null
+++ b/.changeset/surface-routed-checks.md
@@ -0,0 +1,10 @@
+---
+"@design-intelligence/ghost": minor
+---
+
+Add markdown checks (`ghost.check/v1`) in a package's `checks/` directory.
+`ghost checks --surface ` grounds the named surfaces and offers every
+check; the host agent judges which apply. A check binds to the prose it enforces
+through an optional `source:` pointer (a node id with an optional `> Heading`),
+not by surface routing. Ghost selects, grounds, and emits checks; it never runs
+them.
diff --git a/.changeset/teach-terms-by-example.md b/.changeset/teach-terms-by-example.md
new file mode 100644
index 00000000..dcd6fce8
--- /dev/null
+++ b/.changeset/teach-terms-by-example.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Teach the authoring lenses and `incarnation`/essence distinction from a shown node example instead of negative definitions ("not fields", "essence is untagged").
diff --git a/.changeset/thesis-steering-packet.md b/.changeset/thesis-steering-packet.md
new file mode 100644
index 00000000..91d8c55b
--- /dev/null
+++ b/.changeset/thesis-steering-packet.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Reframe the package README and description around the fingerprint as a portable brand steering packet.
diff --git a/.changeset/validate-help-describes-current-checks.md b/.changeset/validate-help-describes-current-checks.md
new file mode 100644
index 00000000..b1bbcf98
--- /dev/null
+++ b/.changeset/validate-help-describes-current-checks.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+Update `ghost validate --help` to describe what validation actually checks today: manifest shape, node validity, material locators, check references, and glossary kind prefixes.
diff --git a/.changeset/validate-surfaces-invalid-nodes.md b/.changeset/validate-surfaces-invalid-nodes.md
new file mode 100644
index 00000000..02352fbe
--- /dev/null
+++ b/.changeset/validate-surfaces-invalid-nodes.md
@@ -0,0 +1,5 @@
+---
+"@design-intelligence/ghost": patch
+---
+
+`ghost validate` now reports a node that fails its own schema instead of silently dropping it from the catalog.
diff --git a/.ghost/.gitignore b/.ghost/.gitignore
new file mode 100644
index 00000000..cffd9e8a
--- /dev/null
+++ b/.ghost/.gitignore
@@ -0,0 +1,2 @@
+.events
+.pulls
diff --git a/.ghost/anti-goal.surface-sprawl.md b/.ghost/anti-goal.surface-sprawl.md
new file mode 100644
index 00000000..cf53be1a
--- /dev/null
+++ b/.ghost/anti-goal.surface-sprawl.md
@@ -0,0 +1,19 @@
+---
+description: Guard against speculative surface area. Review-critical for changes that add commands, flags, fields, or vocabulary.
+materials:
+ - packages/ghost/src/commands/*.ts
+ - .changeset/*.md
+---
+
+Not: shipping a field, flag, node type, hierarchy, or projection axis because
+a future use case might want it. `extends`, `incarnation`, surface routing,
+and the graph model all shipped unused and were all cut.
+
+Instead: ship the smallest surface that solves an observed failure, and cut
+surface that has no user. New CLI commands and flags need a changeset, a
+manifest regeneration (`pnpm dump:cli-help`), and a worked example in the
+skill bundle before they are real.
+
+Recognize the switch: if a PR adds a concept that no recipe teaches and no
+test exercises against a real task, the guard failed. If explaining the
+feature requires new vocabulary, that vocabulary is a cost; count it.
diff --git a/.ghost/composition.yml b/.ghost/composition.yml
deleted file mode 100644
index 295f9e15..00000000
--- a/.ghost/composition.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-patterns:
- - id: sparse-contribution-before-completeness
- kind: structure
- pattern: Ghost scan and docs describe what each package contributes instead of treating absent facets as incomplete packages.
- guidance:
- - Do not require every package to contain intent, inventory, composition, and validate.
- - Treat nested packages as patches in the broad-to-local fingerprint stack.
- - Keep ghost signals as stdout-only reconnaissance for agents, not package material.
- - Curate durable building blocks and exemplars into inventory.yml instead of treating raw signals as inventory authorship.
- - Use Git review for fingerprint approval instead of Ghost-specific draft files.
- evidence:
- - path: README.md
- - path: docs/fingerprint-format.md
- - path: packages/ghost/src/scan/fingerprint-contribution.ts
- - id: compact-agent-handoff
- kind: content
- pattern: CLI and emitted prompts should tell the host agent exactly which fingerprint facets to read and which findings can block.
- guidance:
- - Prefer short ordered workflows over broad conceptual lectures.
- - Name missing fingerprint grounding or facet coverage plainly without creating a separate fingerprint-change workflow.
- evidence:
- - path: packages/ghost/src/context/package-review-command.ts
- - id: editorial-workbench-docs
- kind: visual
- pattern: Ghost docs combine restrained editorial explanation with concrete command and schema work surfaces.
- applies_to:
- scopes:
- - docs-site
- surface_types:
- - docs-home
- - docs-foundation
- - tool-doc
- guidance:
- - Keep concepts plain and examples inspectable.
- - Avoid oversized marketing composition for reference pages.
- check_refs:
- - validate.check:component-pages-use-display-scale
- evidence:
- - path: apps/docs/src/components/docs/page-header.tsx
- - path: apps/docs/src/components/docs/component-page-shell.tsx
- - id: token-first-interface
- kind: visual
- pattern: Ghost UI surfaces use semantic tokens for repeatable color and avoid ad hoc surface hex values.
- applies_to:
- scopes:
- - docs-site
- - ghost-ui-components
- surface_types:
- - component-catalogue
- - docs-foundation
- - docs-home
- - primitive-demo
- - theme-control
- guidance:
- - Define color meaning in the token layer before repeating values in components.
- - Treat one-off literal colors in UI surfaces as review-worthy drift.
- - Using the right token is not enough if hierarchy, disclosure, or operability regresses.
- check_refs:
- - validate.check:no-hardcoded-surface-hex
- evidence:
- - path: packages/ghost-ui/src/styles/main.css
diff --git a/.ghost/decision.flat-corpus.md b/.ghost/decision.flat-corpus.md
new file mode 100644
index 00000000..99b51c5c
--- /dev/null
+++ b/.ghost/decision.flat-corpus.md
@@ -0,0 +1,19 @@
+---
+description: Why the fingerprint is a flat set of prose nodes and not a graph. Gather when tempted to add structure, edges, inheritance, or routing.
+materials:
+ - docs/purposes.md
+---
+
+Chosen: a flat corpus. A node is one markdown file; its kind is a filename
+prefix declared in the glossary; altitude and conditions live in the prose;
+`materials` is the single locator field.
+
+Rejected: the graph model (edges, hubs, spokes), cross-package `extends` and
+inherited nodes, the `incarnation` projection axis, check surface routing,
+and a JSON schema for the artifact. Each added authoring cost and routing
+logic that agents did not need: the agent reading prose against a task
+out-performed every mechanical routing layer we built.
+
+Reversal condition: evidence that agents systematically fail to select the
+right nodes from a flat menu at realistic corpus sizes. Until then, structure
+proposals are surface sprawl (see `anti-goal.surface-sprawl`).
diff --git a/.ghost/glossary.md b/.ghost/glossary.md
new file mode 100644
index 00000000..3f2c4ff7
--- /dev/null
+++ b/.ghost/glossary.md
@@ -0,0 +1,24 @@
+---
+kinds:
+ - name: principle
+ - name: anti-goal
+ posture: guard
+ - name: decision
+---
+
+# principle
+
+Durable stance of the Ghost project: true across the CLI, the skill bundle,
+the docs site, and the README unless a narrower condition in the prose limits
+it.
+
+# anti-goal
+
+What Ghost's own surfaces must never look or sound like: named generic
+patterns and rejected neighbors, each stated with its replacement.
+Review-critical.
+
+# decision
+
+Worked tradeoff trace: what was chosen, what was rejected, why, and what would
+reverse it. Gather when tempted to revisit a settled question.
diff --git a/.ghost/index.md b/.ghost/index.md
new file mode 100644
index 00000000..14edd6b5
--- /dev/null
+++ b/.ghost/index.md
@@ -0,0 +1,29 @@
+---
+description: Always read first. The non-negotiables for anything written or built for the Ghost project itself.
+---
+
+This fingerprint covers Ghost's own surfaces: the CLI output, the skill
+bundle recipes, the docs site, the README, and changesets. It is Ghost
+dogfooding Ghost.
+
+The non-negotiables:
+
+- **Plain, precise, restrained, product-minded.** Every shipped sentence
+ earns its place. No brand-deck filler ("elevate", "delight", "seamless",
+ "empower"), no hype, no exclamation points. See `principle.voice`.
+- **The vocabulary is policed.** `scripts/check-terminology.mjs` bans the
+ memory-era vocabulary and other retired terms across shipped prose,
+ including this fingerprint. Use the current names: fingerprint, node, kind,
+ glossary, corpus, materials, check, gather, pull, pulse, review.
+- **Deterministic CLI, interpretive agent.** The CLI never calls an LLM and
+ never interprets. Anything requiring reading a brand or grading a diff
+ belongs to the host agent via the skill. See
+ `principle.byoa-boundary`.
+- **The fingerprint shape does not bend to consumers.** Flat corpus, filename
+ kinds, prose bodies. Any feature that needs the artifact to change shape
+ for one consumer is a leak. See `docs/purposes.md`.
+
+Where this fingerprint is silent (visual design of the docs site, vessel's
+component aesthetics), proceed from local convention and label the choice
+provisional. For anything that changes the public package's shape or
+vocabulary, ask a human.
diff --git a/.ghost/intent.yml b/.ghost/intent.yml
deleted file mode 100644
index 5f52542d..00000000
--- a/.ghost/intent.yml
+++ /dev/null
@@ -1,140 +0,0 @@
-summary:
- product: Ghost
- audience:
- - OSS maintainers adopting AI-assisted product workflows
- - agents generating or reviewing product UI
- - Ghost contributors
- goals:
- - Keep product-surface composition fingerprints repo-local, portable, and easy for agents to read.
- - Preserve surface composition across generation, review, and remediation.
- - Treat intent, inventory, composition, and validate as sparse package facets.
- - Use Git as the staging and approval boundary for fingerprint edits.
- anti_goals:
- - Treat raw inventory as canonical surface guidance.
- - Turn support files into peers of intent, inventory, and composition.
- - Turn Ghost into a design-system snapshot or screenshot archive.
- - Become a fingerprint lifecycle manager or proposal system.
- - Let advisory review block work without deterministic checks.
- tradeoffs:
- - Prefer compact durable intent over exhaustive surveys.
- - Keep validation checks in the fingerprint package without treating them as generation input.
- - Preserve OSS-friendly language over company-specific strategy.
- tone:
- - plain
- - precise
- - restrained
- - product-minded
-situations:
- - id: capturing-fingerprint
- title: Capturing fingerprint facets
- user_intent: Create or update the Ghost fingerprint for a project.
- product_obligation: Help the agent distinguish sparse fingerprint contributions from optional checks and raw repo signals.
- principles:
- - intent.principle:fingerprint-folder-is-canonical
- - intent.principle:signals-are-authoring-evidence
- - intent.principle:sparse-packages-are-patches
- experience_contracts:
- - intent.experience_contract:git-is-approval-boundary
- patterns:
- - composition.pattern:sparse-contribution-before-completeness
- - id: reviewing-generated-ui
- title: Reviewing generated or changed UI
- user_intent: Know whether a change preserves the product surface.
- product_obligation: Separate deterministic blocking checks from advisory critique.
- principles:
- - intent.principle:checks-are-enforcement
- experience_contracts:
- - intent.experience_contract:review-cites-fingerprint-and-diff
- patterns:
- - composition.pattern:compact-agent-handoff
- - id: documenting-ghost
- title: Explaining Ghost in public docs
- user_intent: Understand the model without internal company context.
- product_obligation: Use OSS-safe language and examples that fit many repo shapes.
- principles:
- - intent.principle:oss-language-is-portable
- patterns:
- - composition.pattern:editorial-workbench-docs
-principles:
- - id: fingerprint-folder-is-canonical
- principle: fingerprint/ is the portable surface-composition package; intent, inventory, composition, and validate are sparse facets.
- guidance:
- - Describe the fingerprint as product-surface composition, not as an operational archive.
- - Keep durable surface intent, hierarchy, behavior, copy, accessibility, trust, and flow in the appropriate facet files.
- - Treat uncommitted or unmerged fingerprint edits as drafts handled by Git, not by a Ghost-specific lifecycle.
- evidence:
- - path: docs/fingerprint-format.md
- note: Public format docs define the split fingerprint folder as the source of truth.
- - path: packages/ghost/src/scan/fingerprint-package.ts
- note: init writes manifest.yml and starter facet files under fingerprint/.
- - id: signals-are-authoring-evidence
- principle: Raw repo signals answer what exists; curated inventory points to the building blocks, sources, and precedents an agent can inspect or use.
- guidance:
- - Use ghost signals as scratch evidence while authoring or refreshing fingerprint facets.
- - Curate durable building blocks, files, routes, libraries, assets, source links, notes, and exemplars into inventory.yml.
- - Do not let signals or building blocks become surface authority without intent and composition.
- evidence:
- - path: README.md
- note: README frames ghost signals as authoring evidence.
- - id: sparse-packages-are-patches
- principle: A fingerprint package contributes only the facets it knows; it does not need to restate inherited context.
- guidance:
- - Treat absent intent, inventory, composition, or validate files as absent local contributions, not automatic incompleteness.
- - Use nested packages to add local patches to the broad-to-local stack.
- - Let scan report contribution state instead of telling users every package must contain every facet.
- evidence:
- - path: packages/ghost/src/scan/fingerprint-contribution.ts
- - path: docs/fingerprint-format.md
- - id: checks-are-enforcement
- principle: Deterministic checks live in fingerprint/validate.yml and should cite typed fingerprint refs when they enforce surface-composition rules.
- guidance:
- - Active checks can block; advisory findings cannot block unless check-backed.
- - Prefer typed refs such as composition.pattern:token-first-interface.
- - Ungrounded checks may exist, but lint should make their missing grounding visible.
- - Checks keep explicit status because gates need lifecycle state.
- evidence:
- - path: packages/ghost/src/ghost-core/checks/lint.ts
- note: Check grounding is validated and missing derivation is reported as a warning.
- - id: oss-language-is-portable
- principle: Public Ghost docs and generated prompts should work for arbitrary OSS repos without internal strategy language.
- guidance:
- - Use examples that fit docs sites, dashboards, SaaS apps, games, and component libraries.
- - Avoid private company concepts in public docs.
- evidence:
- - path: docs/fingerprint-format.md
- - path: docs/generation-loop.md
-experience_contracts:
- - id: review-cites-fingerprint-and-diff
- contract: Advisory review findings must cite the diff location and the relevant fingerprint refs.
- obligations:
- - Use active checks only when a finding should block.
- - Use missing-fingerprint or experience-gap when the fingerprint cannot support a confident finding.
- evidence:
- - path: packages/ghost/src/context/package-review-command.ts
- note: Emitted review command tells agents what to cite.
- - id: git-is-approval-boundary
- contract: Fingerprint edits use ordinary Git workflow; checked-in fingerprint/ files are truth.
- obligations:
- - Do not rewrite canonical fingerprint facets silently during generation or review.
- - Treat uncommitted or unmerged fingerprint edits as draft work.
- - Record durable surface-composition guidance in intent.yml, inventory.yml, and composition.yml; deterministic gates belong in validate.yml.
- evidence:
- - path: packages/ghost/src/skill-bundle/references/capture.md
- - id: emitted-context-prefers-fingerprint-layers
- contract: Emitted context bundles and review commands should prefer split fingerprint facets over legacy survey or markdown artifacts.
- obligations:
- - Default emit paths load the resolved fingerprint package or stack.
- - Legacy direct markdown emit is unsupported for package context.
- evidence:
- - path: packages/ghost/src/context/entrypoint-markdown.ts
- - path: packages/ghost/src/scan-emit-command.ts
- - id: interfaces-preserve-operability
- contract: Ghost interfaces should preserve operability, readable examples, and responsive access before visual polish.
- obligations:
- - Preserve keyboard-reachable controls and readable code examples.
- - Keep docs and reference content readable on narrow screens.
- - Let dense work surfaces collapse controls before hiding primary content.
- - Keep advisory review separate from dedicated accessibility audits.
- evidence:
- - path: docs/generation-loop.md
- - path: apps/docs/src/components/docs/component-page-shell.tsx
diff --git a/.ghost/inventory.yml b/.ghost/inventory.yml
deleted file mode 100644
index 23ad1e9a..00000000
--- a/.ghost/inventory.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-topology:
- scopes:
- - id: public-cli
- paths:
- - packages/ghost/src
- surface_types:
- - cli-command
- - emitted-agent-prompt
- - id: skill-bundle
- paths:
- - packages/ghost/src/skill-bundle
- surface_types:
- - agent-recipe
- - id: docs-site
- paths:
- - docs
- - README.md
- - apps/docs
- surface_types:
- - docs-home
- - docs-foundation
- - tool-doc
- - id: ghost-ui-components
- paths:
- - packages/ghost-ui
- surface_types:
- - component-catalogue
- - primitive-demo
- - theme-control
- surface_types:
- - agent-recipe
- - cli-command
- - component-catalogue
- - docs-foundation
- - docs-home
- - emitted-agent-prompt
- - primitive-demo
- - theme-control
- - tool-doc
-building_blocks:
- tokens:
- - semantic color CSS variables
- - font-display
- - spacing and radius scales
- components:
- - Button
- - CodeBlock
- - ComponentPageShell
- - PageHeader
- libraries:
- - shadcn-style registry primitives
- - lucide icons
- assets:
- - CLI manifest JSON
- - docs code examples
- routes:
- - apps/docs/src/app
- files:
- - README.md
- - docs/fingerprint-format.md
- - docs/generation-loop.md
- - packages/ghost/src/relay.ts
- - packages/ghost/src/context/entrypoint.ts
- - packages/ghost/src/context/entrypoint-markdown.ts
- - packages/ghost/src/context/package-review-command.ts
- notes:
- - Inventory is replaceable material; intent captures what must remain true and composition captures how material is assembled.
- - Correct components or tokens do not prove a surface is aligned.
-exemplars:
- - id: public-readme-fingerprint-model
- path: README.md
- title: Public README fingerprint model
- surface_type: docs-home
- scope: docs-site
- note: Public entry point describes split fingerprint facets and sparse contribution state.
- why: Shows how Ghost explains repo-local sparse fingerprint facets, curated inventory, raw signals, and Git-reviewed approval.
- refs:
- - intent.principle:fingerprint-folder-is-canonical
- - intent.principle:signals-are-authoring-evidence
- - id: review-command-fingerprint-packet
- path: packages/ghost/src/context/package-review-command.ts
- title: Generated review command
- surface_type: emitted-agent-prompt
- scope: public-cli
- note: Review command is generated from split fingerprint intent/inventory/composition.
- why: Shows how Ghost turns fingerprint facets into an agent-facing review instruction.
- refs:
- - intent.experience_contract:review-cites-fingerprint-and-diff
- - composition.pattern:compact-agent-handoff
-sources: []
diff --git a/.ghost/principle.byoa-boundary.md b/.ghost/principle.byoa-boundary.md
new file mode 100644
index 00000000..462e375c
--- /dev/null
+++ b/.ghost/principle.byoa-boundary.md
@@ -0,0 +1,28 @@
+---
+description: The line between the deterministic CLI and the interpreting agent. Gather when adding or changing any CLI command, flag, or emitted packet.
+materials:
+ - packages/ghost/src/cli.ts
+ - docs/purposes.md
+---
+
+Ghost is BYOA: the host agent reads, decides, and writes; the CLI grounds
+that work deterministically. Every feature lands on one side of this line.
+
+The CLI side: validation, the gather menu, node pulls, event tapes, packet
+assembly. Repeatable, no LLM, same output for the same input. Its emissions
+are evidence for an agent, never verdicts: `ghost review` assembles an
+advisory packet; it does not grade the diff. Probes run shell commands and
+report output as evidence only.
+
+The agent side: selecting nodes against a task, interpreting prose,
+grading review packets, authoring fingerprints. This lives in the skill
+bundle, never in CLI code.
+
+When a proposed feature needs the CLI to interpret meaning, rank by
+relevance beyond mechanical matching, or pass/fail on prose, it belongs in
+the skill instead. When a proposed skill step is repeatable and mechanical,
+move it into the CLI so agents cannot do it inconsistently.
+
+A consumer may read the fingerprint through any projection it likes; it may
+not change the shape of the fingerprint to suit itself. The shape test lives
+in `docs/purposes.md`.
diff --git a/.ghost/principle.voice.md b/.ghost/principle.voice.md
new file mode 100644
index 00000000..ec82fff6
--- /dev/null
+++ b/.ghost/principle.voice.md
@@ -0,0 +1,25 @@
+---
+description: How Ghost's own prose sounds. Gather before writing docs, CLI output, README copy, skill recipes, or changesets.
+materials:
+ - README.md
+ - packages/ghost/src/skill-bundle/references/capture.md
+---
+
+Ghost's prose is plain, precise, restrained, and product-minded. When
+completeness and compactness compete, compactness wins; link out for depth
+instead of inlining it.
+
+Concrete over aspirational: name the decision a truth forces, not the value
+it serves. "The CLI does repeatable work with no LLM" beats "we believe in
+deterministic tooling."
+
+Teach terms by example before defining them. A worked node body carries more
+than a paragraph about node bodies; the README leads with a real `.ghost/`
+listing, not a concept diagram.
+
+No em dashes in shipped prose. Short sentences, active voice, present tense.
+Changesets are one sentence, user-facing, present tense.
+
+The vocabulary is versioned and enforced: `scripts/check-terminology.mjs`
+fails the build on retired terms. When renaming a concept, retire the old
+word everywhere in the same change and add it to the forbidden list.
diff --git a/.ghost/validate.yml b/.ghost/validate.yml
deleted file mode 100644
index e29b445f..00000000
--- a/.ghost/validate.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-schema: ghost.validate/v1
-id: ghost
-checks:
- - id: no-hardcoded-surface-hex
- title: Prefer semantic color tokens over inline hex in UI surfaces
- status: proposed
- severity: serious
- derivation:
- composition:
- - composition.pattern:token-first-interface
- applies_to:
- scopes:
- - ghost-ui-components
- - docs-site
- surface_types:
- - component-catalogue
- - docs-foundation
- - docs-home
- detector:
- type: forbidden-regex
- pattern: '#[0-9a-fA-F]{6,8}'
- evidence:
- support: 0.9
- observed_count: 1
- examples:
- - path: packages/ghost-ui/src/styles/main.css
- note: Canonical color values are declared as CSS variables before use.
- repair: Move repeatable UI colors into the semantic token layer in packages/ghost-ui/src/styles/main.css.
- - id: component-pages-use-display-scale
- title: Component catalogue pages keep the editorial display scale
- status: proposed
- severity: nit
- derivation:
- composition:
- - composition.pattern:editorial-workbench-docs
- applies_to:
- scopes:
- - docs-site
- detector:
- type: required-regex
- pattern: font-display
- evidence:
- support: 0.88
- observed_count: 4
- examples:
- - apps/docs/src/components/docs/page-header.tsx
- - apps/docs/src/components/docs/component-page-shell.tsx
- repair: Use the existing display heading helpers or `font-display` heading scale instead of local one-off title styling.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 0ba9db25..9ea1a184 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,4 +1,4 @@
contact_links:
- - name: ❓ Questions and Help 🤔
- url: https://discord.gg/block-opensource (/add your discord channel if applicable)
- about: This issue tracker is not for support questions. Please refer to the community for more help.
+ - name: Questions and discussion
+ url: https://github.com/block/ghost/discussions
+ about: Ask questions and discuss Ghost. The issue tracker is for bugs.
diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml
index 1fc56ceb..2015495d 100644
--- a/.github/workflows/deploy-pages.yml
+++ b/.github/workflows/deploy-pages.yml
@@ -33,13 +33,13 @@ jobs:
- run: pnpm install --frozen-lockfile
- name: Build Ghost CLI package
- run: pnpm --filter @anarchitecture/ghost build
+ run: pnpm --filter @design-intelligence/ghost build
- name: Build ghost-ui library
- run: pnpm --filter ghost-ui build:lib
+ run: pnpm --filter @design-intelligence/vessel build:lib
- name: Build shadcn registry
- run: pnpm --filter ghost-ui build:registry
+ run: pnpm --filter @design-intelligence/vessel build:registry
- name: Build docs site (base=/ghost/)
env:
diff --git a/.github/workflows/release-tarball.yml b/.github/workflows/release-tarball.yml
index dcc6806e..14e92143 100644
--- a/.github/workflows/release-tarball.yml
+++ b/.github/workflows/release-tarball.yml
@@ -1,7 +1,7 @@
name: Publish tarball to GitHub Release
# Manual fallback for the tarball distribution channel. Builds a
-# package-manager-friendly @anarchitecture/ghost archive with runtime
+# package-manager-friendly @design-intelligence/ghost archive with runtime
# dependencies included and attaches the .tgz to a GitHub Release.
#
# The normal path is release.yml, which attaches the tarball automatically on
@@ -43,7 +43,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- name: Build
- run: pnpm --filter @anarchitecture/ghost build
+ run: pnpm --filter @design-intelligence/ghost build
# Keep this distribution archive separate from npm publishing: npm gets
# the normal package, while GitHub Releases get a self-contained archive
@@ -63,7 +63,7 @@ jobs:
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
- TAG="anarchitecture-ghost@$INPUT_VERSION"
+ TAG="design-intelligence-ghost@$INPUT_VERSION"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Create GitHub Release
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 92b3535e..b65ec986 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -38,7 +38,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- - run: pnpm --filter @anarchitecture/ghost build
+ - run: pnpm --filter @design-intelligence/ghost build
- name: Create Release PR or publish
id: changesets
@@ -51,8 +51,8 @@ jobs:
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- NPM_TOKEN: ${{ secrets.GHOST_NPM_PUBLISH_TOKEN }}
- NODE_AUTH_TOKEN: ${{ secrets.GHOST_NPM_PUBLISH_TOKEN }}
+ NPM_TOKEN: ${{ secrets.DESIGN_INTELLIGENCE_NPM_PUBLISH_TOKEN }}
+ NODE_AUTH_TOKEN: ${{ secrets.DESIGN_INTELLIGENCE_NPM_PUBLISH_TOKEN }}
# Changesets publish emits the published package list and skips versions
# already present on npm. Keep GitHub Releases owned here so Homebrew has
@@ -67,8 +67,8 @@ jobs:
run: |
set -euo pipefail
VERSION="$(echo "$PUBLISHED_PACKAGES" \
- | python3 -c "import json,sys; pkgs=json.load(sys.stdin); print(next(p['version'] for p in pkgs if p['name']=='@anarchitecture/ghost'))")"
- TAG="anarchitecture-ghost@${VERSION}"
+ | python3 -c "import json,sys; pkgs=json.load(sys.stdin); print(next(p['version'] for p in pkgs if p['name']=='@design-intelligence/ghost'))")"
+ TAG="design-intelligence-ghost@${VERSION}"
mkdir -p dist-tarball
node scripts/pack-release-tarball.mjs "$GITHUB_WORKSPACE/dist-tarball"
ls -la dist-tarball
@@ -78,7 +78,7 @@ jobs:
gh release upload "$TAG" dist-tarball/*.tgz --clobber
# Export the tag, sha256, and artifact URL so the Homebrew tap can
# pin the exact tarball. The packed filename is dynamic
- # (anarchitecture-ghost-X.Y.Z.tgz), so resolve it from the staging dir.
+ # (design-intelligence-ghost-X.Y.Z.tgz), so resolve it from the staging dir.
archive="$(ls dist-tarball/*.tgz | head -n 1)"
filename="$(basename "$archive")"
sha256="$(shasum -a 256 "$archive" | awk '{print $1}')"
diff --git a/.gitignore b/.gitignore
index b022bb76..0c51af86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@ packages/ghost-ui/.ghost/cache/*
# Local Superpowers planning artifacts
docs/superpowers/plans/
docs/superpowers/specs/
+notes/
+demo/
diff --git a/CLAUDE.md b/CLAUDE.md
index 89dd8174..b4a72c9a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,13 +1,17 @@
# Ghost
-Agents can assemble UI. What they cannot reliably preserve is the product
-surface composition behind that UI: hierarchy, density, restraint, repetition,
-trust, flow, and the choices that make a surface feel intentional.
-
-Ghost keeps that surface composition in a repo-local `.ghost/` fingerprint package. The public npm shape
-is one package, `@anarchitecture/ghost`, with one user-facing bin, `ghost`.
-The CLI validates, computes, compares, and emits deterministic packets. The
-host agent does the interpretive BYOA work through the installed `ghost` skill.
+Agents now do the making — screens, emails, empty states, sentences. What they
+cannot reliably preserve is the brand behind that work: the stance, the
+density, the restraint, the trust moves, and the choices that make an output
+feel intentional. An agent holds nothing it isn't handed.
+
+Ghost hands it the brand as a portable steering packet: a repo-local `.ghost/`
+fingerprint package, a flat corpus of prose nodes read before anything is made. The public npm shape is one package,
+`@design-intelligence/ghost`, with one user-facing bin, `ghost`. The CLI
+validates the corpus, emits the fingerprint menu, pulls selected nodes, records
+local selection events, and assembles advisory review packets from checks. Optional
+review checks attach under `.ghost/checks/`. The host agent does all
+selection and interpretive BYOA work through the installed `ghost` skill.
## Build & Run
@@ -23,103 +27,119 @@ Run the public CLI after building:
```bash
node packages/ghost/dist/bin.js
-pnpm --filter @anarchitecture/ghost exec ghost
+pnpm --filter @design-intelligence/ghost exec ghost
```
## Architecture
Ghost is **BYOA (bring your own agent)**. Claude Code, Codex, Cursor, Goose, or
-another host agent reads, decides, and writes. Ghost is the deterministic
-calculator the agent reaches for: schema validation, repo-signal helpers,
-structural diffs, drift checks, comparison math, and handoff packets.
+another host agent reads, decides, and writes. Ghost grounds that work with a
+deterministic CLI and an interpretive skill bundle. The CLI does repeatable work
+with no LLM: schema and node validation, glossary kind-prefix checks, check
+validation, a flat gather menu, selected-node pulls, local pulse summaries, and
+review packet assembly. The skill teaches the agent how to author and consume
+the fingerprint.
-The canonical root `.ghost/` package follows:
+The canonical root `.ghost/` package is a flat set of prose nodes plus optional
+checks:
```text
-manifest.yml
-intent.yml
-inventory.yml
-composition.yml
-validate.yml
+manifest.yml # schema + id (the package anchor)
+glossary.md # the author's category vocabulary + what each kind means
+..md # a brand truth of a declared kind (principle.density.md)
+.md # an uncategorized brand truth (voice.md)
+checks/ # optional review assertions; never a node source
```
-The three root facet files are the core model:
-
-- `intent.yml` for surface intent.
-- `inventory.yml` for curated material, exemplars, and source links.
-- `composition.yml` for experience patterns.
-
-`validate.yml` validates output through deterministic checks; it is not
-generation input.
-Ordinary Git review is the approval boundary for fingerprint edits.
-
-Legacy `resources.yml`, `map.md`, `survey.json`, and `patterns.yml` may still
-appear in older repos or as migration source material. They are not canonical
-fingerprint input for new Ghost work.
+The **corpus is flat**. A node is a markdown file: a `description` in
+frontmatter (the retrieval payload), optional `materials`, and a brand truth in
+the prose body. A node's identity is its filename minus `.md`; its kind is the
+filename prefix before the first dot, declared in the glossary. There is no
+hierarchy, no inheritance, no edges; nesting into folders is a browsing
+convenience only.
+
+`materials` is the single locator field for concrete materials the truth is
+about. It accepts repo-relative paths/globs and absolute HTTPS URLs. Components,
+patterns, logos, motion files, illustrations, and external asset libraries all
+use the same field. Guidance stays in prose; `materials` only says where the
+material is.
+
+While drafting a body, ask three questions of every truth (drafting prompts,
+never fields): **why** (the stance), **with what** (the materials, and pointers
+to implementation or assets the agent can inspect), and **how it is assembled**
+(the patterns that make the output feel intentional).
+
+Altitude lives in the prose: a universal truth is stated plainly; a narrower
+truth names its **condition**, the situation it applies in, never a filing
+destination. `ghost gather` emits the whole menu (every node's id, kind,
+description, and material count); the agent selects just-in-time against the
+actual task. `ghost pull` emits selected node bodies and materials. `ghost
+review` reads a diff, matches touched files to node materials, offers relevant
+checks, and emits an advisory packet for the host agent to judge.
+
+**Checks** (`.ghost/checks/*.md`) are optional review assertions that declare
+`references` to fingerprint node ids (with optional heading anchors) and prose
+instructions for the reviewing agent. Checks are feed-back only and never leak
+into generation context. Scaffold them with `ghost checks init` or `ghost init
+--with checks`. Ordinary Git review is the approval boundary for fingerprint
+edits and checks.
## Packages
| Package | Published? | Description |
| --- | --- | --- |
-| `packages/ghost` | yes: `@anarchitecture/ghost` | Unified public package. Ships the `ghost` CLI, fingerprint package authoring, checks, advisory review packets, comparison, drift stance verbs, and the unified skill bundle. |
-| `packages/ghost-core` | no | Private historical shared package. Runtime code needed by npm is folded into `packages/ghost/src/ghost-core`. |
-| `packages/ghost-fleet` | no | Private fleet view across many Ghost bundles. Consumes workspace exports from `@anarchitecture/ghost`. |
-| `packages/ghost-ui` | no | Reference design system: shadcn registry plus `ghost-mcp` MCP server. |
+| `packages/ghost` | yes: `@design-intelligence/ghost` | The public package. Ships the `ghost` CLI, node authoring, corpus validation, gather/pull/pulse, review packet assembly, and the unified skill bundle. Shared runtime lives in `packages/ghost/src/ghost-core`. |
+| `packages/vessel` | no | A standalone shadcn component registry plus `vessel-mcp` MCP server: the opinionated default reference body. Design-system-agnostic; nothing in Ghost requires it. |
| `apps/docs` | no | Docs site. |
## CLI Commands
+Core workflow:
+
+| Command | Description |
+| --- | --- |
+| `ghost init` | Scaffold `.ghost/` with a manifest, starter glossary, and a starter `index.md`. `--with checks` also adds the checks directory. |
+| `ghost checks init` | Scaffold `.ghost/checks/` with an example review assertion. |
+| `ghost validate` | Validate the package: manifest shape, node validity, material locators, check references, and glossary kind prefixes. |
+| `ghost gather [ask…]` | Emit the fingerprint menu for the agent to select from. |
+| `ghost pull […]` | Emit selected nodes' bodies and materials; append the selection to the local `.ghost/.events` tape. |
+| `ghost review` | Emit an advisory review packet for a diff using material-backed nodes and checks (requires `.ghost/checks/`). |
+| `ghost pulse` | Summarize local gather/pull events from `.ghost/.events`. |
+| `ghost skill install` | Install the unified `ghost` skill bundle. |
+
+Advanced/maintenance:
+
| Command | Description |
| --- | --- |
-| `ghost init` | Create `.ghost/` with manifest, facets, and deterministic checks. |
-| `ghost scan` | Report fingerprint contribution facets and BYOA next-step guidance. |
-| `ghost signals` | Emit raw repo signals as JSON for fingerprint authoring. |
-| `ghost lint` | Validate a fingerprint package or single artifact. |
-| `ghost verify` | Validate fingerprint evidence and exemplar paths, and typed check refs. |
-| `ghost describe` | Print direct markdown section ranges. |
-| `ghost diff` | Structural direct-fingerprint prose diff between direct fingerprints. |
-| `ghost survey ` | Legacy survey helpers for optional `ghost.survey/v1` workflows. |
-| `ghost check` | Run active `ghost.validate/v1` deterministic gates against a diff. |
-| `ghost review` | Emit an evidence-routed advisory review packet grounded in fingerprint facets, inventory exemplars, checks, and the diff. |
-| `ghost compare` | Pairwise or composite comparison over packages or direct fingerprints. |
-| `ghost ack` | Record stance toward the tracked fingerprint in `.ghost-sync.json`. |
-| `ghost track` | Shift the tracked fingerprint. |
-| `ghost diverge` | Declare intentional divergence on a dimension. |
-| `ghost emit ` | Emit `review-command`. |
-| `ghost skill install` | Install the unified `ghost` agentskills.io bundle. |
-
-`ghost scan --format json` is deterministic contribution and source-signal state.
-It does not run an LLM.
+| `ghost manifest` | Emit a self-describing JSON manifest of every command and flag. |
## Public Exports
-- `@anarchitecture/ghost` for the combined surface.
-- `@anarchitecture/ghost/scan` for scan contribution, source signals, and stack discovery.
-- `@anarchitecture/ghost/fingerprint` for fingerprint package authoring, linting, verification, parsing, and serialization.
-- `@anarchitecture/ghost/drift` for check/review/compare/stance helpers.
-- `@anarchitecture/ghost/core` for shared schemas, types, and loaders.
-- `@anarchitecture/ghost/cli` for `buildCli()`.
+- `@design-intelligence/ghost` for the combined surface.
+- `@design-intelligence/ghost/scan` for package-path resolution helpers.
+- `@design-intelligence/ghost/fingerprint` for node package authoring, validation, parsing, and serialization.
+- `@design-intelligence/ghost/core` for shared schemas, types, and loaders.
+- `@design-intelligence/ghost/cli` for `buildCli()`.
## Environment Variables
No API key is required to run Ghost. Optional variables:
-- `OPENAI_API_KEY` / `VOYAGE_API_KEY` are consumed only by semantic embedding
- helpers when a host opts into enriched comparison.
+- `GHOST_PACKAGE_DIR` selects a custom package directory (or pass `--package`).
Each CLI auto-loads `.env` and `.env.local` from the working directory.
## Releasing & Changesets
-`@anarchitecture/ghost` is the only public package. Private packages are
-ignored by Changesets.
+`@design-intelligence/ghost` is the only public package. Private packages
+are ignored by Changesets.
When an agent completes a user-visible change to the public package, write a
changeset file instead of asking the user to run `pnpm changeset`:
```markdown
---
-"@anarchitecture/ghost": patch
+"@design-intelligence/ghost": patch
---
One sentence, user-facing, present tense.
@@ -132,8 +152,11 @@ Use `patch` for fixes and docs, `minor` for new commands/flags/exports, and
- Keep publishable runtime code self-contained in `packages/ghost`; no
`workspace:*` runtime dependencies in the packed public artifact.
-- The canonical on-disk form is a flat `.ghost/` package.
-- Direct `fingerprint.md` remains only for legacy/direct compare workflows.
+- The canonical on-disk form is a flat `.ghost/` package: `manifest.yml` plus
+ `glossary.md` plus prose nodes (`..md` or bare `.md`) plus
+ an optional `.ghost/checks/` directory. The corpus is flat; kinds come
+ from filename prefixes declared in the glossary, never from a separate
+ declaration or a directory hierarchy.
- Skill recipes live in `packages/ghost/src/skill-bundle/references/`; install
them with `ghost skill install`.
- The CLI manifest at `apps/docs/src/generated/cli-manifest.json` is generated
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..a5140d80
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,66 @@
+# Contributing to Ghost
+
+Ghost is pre-1.0 and moving fast. This doc is organized around what gets a
+PR declined, because that's the information you actually need. Everything
+below is enforced — by review, by CI, or both.
+
+## Invariants
+
+These are project-defining. A PR that violates one gets declined regardless
+of quality elsewhere.
+
+1. **The CLI computes; it never decides.** No LLM calls in the CLI, ever.
+ Ghost is BYOA: the host agent does all interpretive work. The CLI's value
+ is determinism — same input, same output, no key required.
+2. **The fingerprint corpus stays flat.** No hierarchy, no inheritance, no
+ edges between nodes. A node's identity is its filename; its kind is a
+ glossary-declared prefix. Altitude lives in prose, not structure.
+3. **Feed-back never leaks into generation context.** Checks and
+ review packets inform the agent after the fact. They are never a node
+ source and never enter `gather`/`pull` output.
+4. **The published artifact packs self-contained.** No `workspace:*` runtime
+ dependencies in `packages/ghost`. CI checks the packed tarball.
+
+## The vocabulary gate
+
+`scripts/check-terminology.mjs` fails CI on banned phrasings. The script is
+the source of truth; these are the clusters and their reasons:
+
+- **The "memory" compounds.** Ghost holds nothing — an agent holds nothing it
+ isn't handed. The fingerprint is a steering packet, not recall.
+- **The "judg-" words.** The CLI computes; the host agent decides. Review
+ output is advisory.
+- **Product-UI-centric framing.** Ghost is brand through every medium —
+ screens, emails, empty states, sentences — so phrases that anchor it to
+ product UI alone are banned.
+- **Bare next-major version markers.** No speculative roadmap language in
+ the tree.
+
+Run `pnpm check:terminology` locally before pushing; don't try to memorize
+the list.
+
+## The deterministic path
+
+- pnpm 10+, Node 20.19+ or 22.12+.
+- `pnpm install` → `pnpm check` → `pnpm test` → `pnpm build`. CI gates:
+ biome lint/format, typecheck, file-size, terminology, CLI-manifest drift,
+ docs frontmatter, packed-tarball checks.
+- Added, removed, or renamed a CLI command or flag? Run `pnpm dump:cli-help`
+ and commit the regenerated manifest, or the drift check declines for you.
+- **Changesets:** `@design-intelligence/ghost` is the only public
+ package. Write the changeset file yourself — `patch` for fixes and docs,
+ `minor` for new commands/flags/exports, `major` for removed or renamed
+ public behavior. One sentence, user-facing, present tense.
+
+## Dogfooding
+
+Surfaces in this repo carry their own fingerprints: `apps/docs/.ghost` and
+`packages/vessel/.ghost`. If you touch a surface, `ghost gather` from its
+fingerprint before you build, and expect review against it. This is where
+you experience Ghost as a user — treat friction you hit here as a bug worth
+reporting.
+
+## Governance
+
+This repo follows [Block's org-level governance](https://github.com/block/.github/blob/main/GOVERNANCE.md).
+Licensed Apache-2.0.
diff --git a/README.md b/README.md
index f9a2c89a..4a5d326b 100644
--- a/README.md
+++ b/README.md
@@ -1,189 +1,195 @@
# Ghost
-**Ghost captures the composition of a product surface: the intent behind it,
-the materials it draws from, and the patterns that make it feel intentional.**
-
-Ghost gives AI agents a checked-in product fingerprint they can read before
-they generate UI and validate after they change it. The public package is
-`@anarchitecture/ghost`, and it installs one CLI: `ghost`.
-
-Agents can assemble components. What they need help preserving is the product
-surface behind those components: hierarchy, density, restraint, behavior, copy,
-accessibility, trust, and flow. Ghost keeps that surface composition in a
-portable `.ghost/` package that ordinary Git review can approve.
-
-## The Shape
-
-The canonical package is intentionally small:
+**Ghost is your brand, packed for agents.** A `.ghost/` folder of plain-prose
+truths — your stance, your voice, your trust moves — checked into the repo and
+read by any agent before it makes anything: a screen, an email, an empty
+state, a sentence.
```text
.ghost/
- manifest.yml # ghost.fingerprint-package/v1 anchor
- intent.yml # surface intent
- inventory.yml # curated material and exemplars
- composition.yml # patterns, flows, states, and arrangements
- validate.yml # optional deterministic gates
+ glossary.md # your vocabulary
+ principle.trust.md # "near the moment of payment, reduce felt risk…"
+ voice.md # how the brand talks
+ asset.logo.md # points at the actual SVGs
```
-A package can be sparse: it contributes whichever facets are locally true. Generation usually uses intent + inventory + composition:
+Today those decisions live in reviewers' heads — "that's not our voice," again,
+on every surface. The agent that built the thing never saw them. Ghost writes
+them down once, where the agent looks first.
-- `intent.yml` says what the surface is trying to do and for whom.
-- `inventory.yml` points agents at materials they can inspect or reuse.
-- `composition.yml` captures the patterns that make those materials feel like
- one intentional product.
+One portable packet; Claude Code, Codex, Cursor, and Goose all read the same
+one. One package, `@design-intelligence/ghost`. One CLI, `ghost`.
-`validate.yml`, nested packages, custom host-wrapper package locations, and raw repo
-signals are supporting features. They do not replace curated fingerprint
-facets. `ghost signals` answers what exists; curated fingerprint facets answer
-what the surface is trying to preserve.
+## Thesis
-Older `resources.yml`, `map.md`, `survey.json`, `patterns.yml`, and direct
-`fingerprint.yml` artifacts can still inform migration workflows, but new Ghost
-work should target `.ghost/`.
+Agents changed the unit of design work. When they make the screens, the
+emails, and the sentences, polishing any one of them moves nothing; the next
+generation starts from the model's average again. The work that compounds is
+architectural: decide where that average serves, decide where the brand must
+win, and put those decisions where the agent reads before it makes.
-## Project Status: Beta
+Ghost is that artifact: a fingerprint checked into the repo, carrying the
+truths, the materials they point at, and the conditions they hold under.
+Buttons stay buttons. The moments that carry your brand get your stance
+instead of the default. The few author it once. Every agent it travels to
+builds from it.
-> [!WARNING]
-> Ghost is pre-1.0 and under active development. The CLI, fingerprint schema,
-> on-disk `.ghost/` package shape, and public JavaScript exports may
-> change in breaking ways before a stable 1.0 release.
->
-> Breaking changes may ship in minor versions while Ghost is pre-1.0. Patch
-> versions are reserved for fixes that should not require migration. If you adopt
-> Ghost today, expect some churn, pin the version you depend on, and review
-> release notes before upgrading.
+[Documentation](https://block.github.io/ghost/) · [npm](https://www.npmjs.com/package/@design-intelligence/ghost)
## Install
```bash
-npm install -D @anarchitecture/ghost
-npx ghost --help
-npx ghost --help --all
+npm install -D @design-intelligence/ghost
+npx ghost skill install
```
-`ghost --help` shows the core workflow. `ghost --help --all` shows the complete
-command index, and each command supports `ghost --help`.
+Every `ghost` command is also available as `ghost-fingerprint` when another tool
+on your machine owns the `ghost` bin.
-Install the BYOA skill bundle so Codex, Claude, Cursor, Goose, or another host
-agent knows how to author and use the fingerprint:
+## Use It
-```bash
-npx ghost skill install
-# or choose an explicit destination
-npx ghost skill install --dest ~/.codex/skills/ghost
-```
-
-Then ask your agent in plain English:
+Ghost is **bring-your-own-agent**. Install the skill bundle so Claude Code,
+Codex, Cursor, Goose, or another host agent knows how to author and use the
+fingerprint, then ask in plain English:
```text
Set up the Ghost fingerprint for this repo.
+Write down the decision I keep repeating about checkout.
Brief this work from the Ghost fingerprint.
-Review this PR against the Ghost fingerprint.
-Compare these two Ghost bundles.
+Review this diff against the Ghost checks.
```
-## Author A Fingerprint
+The CLI does deterministic work. The agent does interpretation.
-Ghost authoring is a human-plus-agent workflow. The CLI creates, inspects, and
-validates the package; the host agent interviews, reads the repo, drafts facet
-edits, and asks you to curate the claims.
+## The Loop
```bash
-ghost init
-ghost init --package product-surface
-ghost scan --format json
-ghost signals .
-ghost lint .ghost
-ghost lint product-surface
-ghost verify .ghost --root .
+ghost init # scaffold .ghost/ with the steering starter (fingerprint only)
+ghost checks init # opt in to review assertions
+ghost validate # check package shape, nodes, materials, and checks
+ghost gather # before building: list every node for this task
+ghost pull # read the picked truths' full bodies
+ghost review # during review: assemble a diff + matched nodes + checks packet
+ghost export # package the fingerprint as a portable artifact with a locator audit
+ghost pulse # while tuning: summarize local gather/pull events
```
-Use `--reference` when a reference library should seed inventory, `--scope`
-for nested product areas, or `--package ` when initializing an exact
-package directory such as `product-surface/`.
-For monorepos, `ghost init --monorepo` creates or preserves the root package,
-detects workspace child roots, and prints proposed `ghost init --scope ...`
-commands by default. Run `ghost init --monorepo --apply` to create the detected
-child packages. Host wrappers that need Ghost files somewhere other than
-`.ghost` may set `GHOST_PACKAGE_DIR=` on the child `ghost`
-process. Exact `--package ` values win over the environment default.
+`gather` and `pull` append structured JSONL events to `.ghost/.events`, a gitignored events tape. That's for tuning descriptions and seeing what agents reached
+for. The events tape is local tuning signal, not canonical fingerprint state.
-Drafted fingerprint edits are just ordinary file changes until Git review
-accepts them. Checked-in Ghost facet files are the Ghost source of truth.
+## CLI Commands
-## Generate From Ghost
+| Command | Description |
+| --- | --- |
+| `ghost init` | Scaffold `.ghost/` with the steering starter: manifest, glossary, `index.md`, and demo nodes for stance, composition, anti-goals, patterns, exemplars, materials, and decisions. `--template minimal` writes only the small manifest/glossary/index starter. `--with checks` also adds the checks directory. |
+| `ghost checks init` | Scaffold `.ghost/checks/` with an example review assertion. |
+| `ghost validate` | Validate the package: manifest, node validity, material locators, check references, and glossary kind prefixes. |
+| `ghost gather [ask…]` | Emit the fingerprint menu for the agent to select from; log the exposed ids. Wild-posture kinds are excluded by default; `--wild` opts in. |
+| `ghost pull […]` | Emit selected node bodies and material locators; log selected and missed ids. |
+| `ghost review` | Emit an advisory review packet from a diff, matched material-backed nodes, and checks (requires `.ghost/checks/`). |
+| `ghost export` | Package the fingerprint as a portable artifact with a locator audit. |
+| `ghost pulse` | Summarize local gather/pull events: abandoned gathers, hit rates, cold nodes, and misses. |
+| `ghost skill install` | Install the skill bundle for your host agent. |
+| `ghost manifest` | Emit a self-describing JSON manifest of commands and flags _(advanced)_. |
-Before generating or revising UI, gather the Relay brief for the target path:
+Run `ghost --help` for the core workflow, `ghost --help --all` for everything,
+and `ghost --help` for flags.
-```bash
-ghost relay gather apps/checkout/review/page.tsx
+## How It Works
+
+A fingerprint is a small folder of prose. The CLI computes; your agent reads,
+writes, and decides.
+
+```text
+.ghost/
+ manifest.yml # schema + package id (the anchor)
+ glossary.md # your kind vocabulary + what each kind means
+ index.md # the curated front door
+ principle.trust.md # a brand truth of kind `principle`
+ asset.logo.md # a truth that may point at concrete materials
+ checks/ # optional review assertions; never nodes
+```
+
+The fingerprint is a **flat set of nodes**. A node is one markdown file: a
+`description` in frontmatter, optional `materials`, and a brand truth in the
+prose body.
+
+```markdown
+---
+description: Logo lockups, clearspace, and when the glyph can stand alone.
+materials:
+ - brand/logo*.svg
+ - https://figma.com/file/example?node-id=logo-lockups
+---
+
+Use the full lockup when recognition matters. Use the glyph only when space is
+constrained or when brand presence should recede.
```
-Relay compiles selected context from the resolved stack as context hits:
-fingerprint refs, why they matched, suggested reads, omissions, and gaps.
-The important shift is timing: Ghost gives agents surface-composition context
-before they build, not only after a review finds drift.
+`materials` is one list of locators for the concrete stuff the truth is about:
+repo-relative paths/globs or absolute HTTPS URLs. Components, patterns, logos,
+motion files, illustrations, and external asset libraries all use the same field.
+Guidance stays in prose; `materials` only says where the material is.
-After implementation, run the deterministic and advisory workflows against the
-same fingerprint:
+**Checks** are optional review assertions in a flat `.ghost/checks/` directory.
+Core `ghost init` ships no checks; add them explicitly:
```bash
-ghost check --base main
-ghost review --base main
+ghost checks init
```
-`ghost check` runs active `ghost.validate/v1` gates and can fail. `ghost review`
-emits an evidence-routed advisory packet for a human or host adapter to use.
+Checks are not nodes. They are review assertions used by `ghost review`:
+
+```markdown
+---
+name: logo-clearspace-holds
+description: Logo usage preserves clearspace, lockup integrity, and glyph rules.
+severity: medium
+references:
+ - asset.logo
+---
+
+Grade whether the change preserves the logo guidance in `asset.logo`.
+```
+
+`ghost gather` and `ghost pull` are feed-forward. `ghost review` is feed-back: it
+reads a diff, matches touched files to node `materials`, offers relevant checks,
+and emits an advisory packet for the host agent to weigh.
+
+Reserved at the package root: `manifest.yml`, `glossary.md`, and `checks/`.
+Every other `*.md` is a node. Renaming a node changes its id.
-## Compare And Govern
+The packet is the product; the CLI is the courier. Everything above —
+gather, pull, review, checks, the events tape — is machinery around the
+fingerprint, and the fingerprint outlives all of it.
-Advanced workflows remain available when a repo needs package stacks,
-comparison, or explicit drift stance:
+## Portable by Design
+
+The fingerprint travels. It is agent-agnostic (every host agent reads the same
+packet), medium-agnostic (the same truths steer a screen, a page, an email, a
+sentence), and repo-native (it moves with a clone, a fork, a new hire's first
+checkout). When you need it as a standalone artifact:
```bash
-ghost stack apps/checkout/review/page.tsx
-ghost compare market/.ghost dashboard/.ghost
-ghost ack --stance aligned --reason "Initial baseline"
-ghost track new-tracked.fingerprint.md
-ghost diverge typography --reason "Editorial product uses a different type scale"
-ghost emit review-command --path apps/checkout/review/page.tsx
+ghost export # package the fingerprint as a portable brand artifact, with a locator audit
```
-`ghost scan --format json` emits deterministic contribution state for `intent`,
-`inventory`, `composition`, and `validate`. A sparse package can be useful with
-only one contributing facet; absent facets may be inherited from broader stack
-context. It does not call an LLM.
+The export audits every `materials` locator so the packet doesn't silently
+point at things that moved.
-## CLI Commands
+## Project Status: Beta
-| Command | Description |
-| --- | --- |
-| `ghost init` | Create `.ghost/` package facet files. |
-| `ghost scan` | Report sparse fingerprint contribution facets. |
-| `ghost lint` | Validate a fingerprint package or individual artifact. |
-| `ghost verify` | Validate evidence paths, exemplar paths, and typed check refs. |
-| `ghost check` | Run active deterministic gates against a diff. |
-| `ghost review` | Emit an evidence-routed advisory packet from fingerprint facets and a diff. |
-| `ghost relay gather` | Gather fingerprint-grounded context for an agent target. |
-| `ghost emit ` | Emit `review-command` artifacts. |
-| `ghost skill install` | Install the unified Ghost skill bundle. |
-| `ghost stack` | Inspect resolved root-to-leaf fingerprint stacks. |
-| `ghost signals` | Emit raw repo signals as JSON for fingerprint authoring. |
-| `ghost describe` | Print markdown section ranges. |
-| `ghost compare` | Compare fingerprint packages. |
-| `ghost ack` / `track` / `diverge` | Record stance toward tracked drift. |
-| `ghost diff` / `survey` | Maintain direct markdown fingerprints or survey/cache files for compatibility workflows. |
+> [!WARNING]
+> Ghost is pre-1.0 and under active development. The CLI, fingerprint schema,
+> on-disk `.ghost/` package shape, and public JavaScript exports may change in
+> breaking ways before a stable 1.0 release.
## Repo Layout
-Ghost is a pnpm monorepo. The public package is self-contained for npm; private
-workspace packages remain development context.
-
| Path | Role | Published? |
| ---- | ---- | --- |
-| [`packages/ghost`](./packages/ghost) | Public package. Ships the `ghost` CLI, folded core runtime, fingerprint package helpers, deterministic checks, advisory review packets, comparison/stance helpers, and the unified skill bundle. | yes: `@anarchitecture/ghost` |
-| [`packages/ghost-fleet`](./packages/ghost-fleet) | Private fleet view across many Ghost bundles. | no |
-| [`packages/ghost-ui`](./packages/ghost-ui) | Reference design system: shadcn registry plus `ghost-mcp` MCP server. | no |
+| [`packages/ghost`](./packages/ghost) | The public `ghost` CLI, node authoring, corpus validation, gather/pull, review packet assembly, and the skill bundle. | yes: `@design-intelligence/ghost` |
+| [`packages/vessel`](./packages/vessel) | A standalone shadcn component registry plus `vessel-mcp` MCP server. | no |
| [`apps/docs`](./apps/docs) | Docs site. | no |
## Development
@@ -194,20 +200,10 @@ pnpm build
pnpm test
pnpm check
pnpm dump:cli-help
-pnpm --filter @anarchitecture/ghost pack
```
-No API key is required to run Ghost. `OPENAI_API_KEY` / `VOYAGE_API_KEY` are
-optional and only used by semantic embedding helpers when a host opts in.
+No API key is required to run Ghost.
-## Resources
+## License
-| Resource | Description |
-| --- | --- |
-| [docs/fingerprint-format.md](./docs/fingerprint-format.md) | Portable `.ghost/` package format. |
-| [docs/generation-loop.md](./docs/generation-loop.md) | Brief, generate, check, review, and remediate loop. |
-| [docs/language-fingerprints.md](./docs/language-fingerprints.md) | Voice and language capture through existing fingerprint facets. |
-| [docs/host-adapters.md](./docs/host-adapters.md) | Adapter-neutral JSON, severity mapping, and custom fingerprint directories. |
-| [docs/ghost-fleet.md](./docs/ghost-fleet.md) | Current private fleet package model. |
-| [GOVERNANCE.md](./GOVERNANCE.md) | Project governance. |
-| [LICENSE](./LICENSE) | Apache License, Version 2.0. |
+[Apache License 2.0](./LICENSE) · [Governance](./GOVERNANCE.md)
diff --git a/apps/docs/.ghost/.gitignore b/apps/docs/.ghost/.gitignore
new file mode 100644
index 00000000..cffd9e8a
--- /dev/null
+++ b/apps/docs/.ghost/.gitignore
@@ -0,0 +1,2 @@
+.events
+.pulls
diff --git a/apps/docs/.ghost/anti-goal.generated-docs-site.md b/apps/docs/.ghost/anti-goal.generated-docs-site.md
new file mode 100644
index 00000000..e5134c6b
--- /dev/null
+++ b/apps/docs/.ghost/anti-goal.generated-docs-site.md
@@ -0,0 +1,28 @@
+---
+description: The generic generated docs and AI-product defaults this site rejects — read before adding any UI, visual section, landing page, or marketing copy.
+materials:
+ - apps/docs/src/app/page.tsx
+ - apps/docs/src/components/docs/hero.tsx
+ - apps/docs/src/components/docs/doc-prose.tsx
+---
+
+Ghost is not the generated AI SaaS docs site.
+
+Reject these defaults even when they are easy to build:
+
+- Gradient hero text, purple/blue glows, aurora backgrounds, particle fields,
+ blurred blobs, and "AI sparkle" ornament.
+- Rounded-xl feature-card grids on gray-50 with three benefit icons and a fake
+ dashboard screenshot.
+- Mascots, emoji headings, playful empty-state illustrations, or robot metaphors.
+- Overpromising copy: "ship on-brand every time," "brand autopilot," "AI that
+ understands your design system," or any claim that makes Ghost sound like it
+ replaces taste, review, or human curation.
+- Decorative motion that exists only because the page felt static.
+- Dense nav chrome, sidebars on landing pages, or tool-index pages that feel like
+ enterprise documentation portals.
+
+A technically correct output can still be off-brand if it looks like any AI tool
+could ship it. Ghost's docs win by being severe, specific, and a little haunted:
+black-and-white, exact language, spectral geometry, and enough restraint that the
+one strange gesture actually lands.
diff --git a/apps/docs/.ghost/asset.docs-materials.md b/apps/docs/.ghost/asset.docs-materials.md
new file mode 100644
index 00000000..c2595c21
--- /dev/null
+++ b/apps/docs/.ghost/asset.docs-materials.md
@@ -0,0 +1,43 @@
+---
+description: Concrete docs-site materials — inspect before inventing tokens, components, typography, navigation, motion, routes, or command examples.
+materials:
+ - apps/docs/src/App.tsx
+ - apps/docs/src/main.tsx
+ - apps/docs/src/styles/dev-fonts.css
+ - apps/docs/src/components/docs/**
+ - apps/docs/src/app/**/*.tsx
+ - apps/docs/src/content/docs/*.mdx
+ - apps/docs/src/generated/cli-manifest.json
+ - packages/vessel/src/styles/main.css
+ - packages/vessel/src/components/**
+---
+
+Do not invent docs-site vocabulary before inspecting the materials already in
+place.
+
+Material contract:
+
+- **Tokens and primitives** come from `@design-intelligence/vessel/styles.css`,
+ backed by `packages/vessel/src/styles/main.css`. Author with semantic roles
+ (`bg-background`, `text-foreground`, `text-muted-foreground`, `border-border`,
+ `bg-card`) and the named docs typography variables (`--heading-*`,
+ `--label-*`). Avoid raw palette utilities unless they are already part of a
+ documented semantic exception.
+- **Fonts** are overridden for the docs dev surface in
+ `apps/docs/src/styles/dev-fonts.css`; `font-display` and `font-sans` resolve to
+ Cash Sans locally. Preserve the uppercase label and black display-type rhythm.
+- **Navigation** is the bottom floating `Dock`. New top nav, side nav, or
+ secondary persistent chrome should be treated as a product decision, not a
+ routine addition.
+- **Page structure** lives in `AnimatedPageHeader`, `SectionWrapper`,
+ `DocsPageLayout`, `DocSection`, and `DocProse`. Use these before making a new
+ layout primitive.
+- **Docs content** is MDX under `apps/docs/src/content/docs/` with frontmatter
+ wired through the docs manifest and routes. Do not hard-code a one-off article
+ route unless the content model cannot express it.
+- **CLI examples** should be checked against `apps/docs/src/generated/cli-manifest.json`
+ or regenerated with `pnpm dump:cli-help` after CLI command/flag changes.
+
+The materials locate facts; this node does not make every existing choice a
+brand truth. If a component repeats a weak or legacy pattern, fix the pattern
+instead of canonizing it here.
diff --git a/apps/docs/.ghost/checks/cli-examples-match-manifest.md b/apps/docs/.ghost/checks/cli-examples-match-manifest.md
new file mode 100644
index 00000000..bc938bcd
--- /dev/null
+++ b/apps/docs/.ghost/checks/cli-examples-match-manifest.md
@@ -0,0 +1,16 @@
+---
+name: cli-examples-match-manifest
+description: CLI examples and generated docs stay synchronized with the real Ghost command surface.
+severity: high
+references:
+ - condition.cli-reference-exactness
+ - asset.docs-materials
+---
+
+When a change touches CLI docs, generated command help, command implementation,
+or examples in MDX, verify that documented command names and flags exist in the
+current CLI manifest or command help. If command metadata changed, require
+`pnpm dump:cli-help` so `apps/docs/src/generated/cli-manifest.json` is updated.
+
+Flag speculative flags, obsolete command names, or examples that describe a skill
+workflow as though it were a CLI command.
diff --git a/apps/docs/.ghost/checks/no-generic-ai-ornament.md b/apps/docs/.ghost/checks/no-generic-ai-ornament.md
new file mode 100644
index 00000000..f8bafddb
--- /dev/null
+++ b/apps/docs/.ghost/checks/no-generic-ai-ornament.md
@@ -0,0 +1,18 @@
+---
+name: no-generic-ai-ornament
+description: Docs UI avoids generic AI SaaS ornament and preserves Ghost's restrained monochrome visual stance.
+severity: medium
+references:
+ - anti-goal.generated-docs-site
+ - principle.visual-composition
+---
+
+Review changes to docs UI surfaces for generic AI-product decoration. Flag newly
+introduced gradient hero text, purple or blue glows, aurora backgrounds,
+particle fields, blurred blobs, emoji headings, mascot or robot metaphors, fake
+product screenshots, or decorative motion that does not clarify reading order or
+state.
+
+This check should not block legitimate semantic color in docs prose or existing
+Vessel component behavior. The issue is generic ornament that weakens the severe,
+monochrome, typographic Ghost stance.
diff --git a/apps/docs/.ghost/checks/review-stays-advisory.md b/apps/docs/.ghost/checks/review-stays-advisory.md
new file mode 100644
index 00000000..66f16490
--- /dev/null
+++ b/apps/docs/.ghost/checks/review-stays-advisory.md
@@ -0,0 +1,16 @@
+---
+name: review-stays-advisory
+description: Docs keep checks and review positioned as feed-back advisory context, never deterministic brand verdict or generation input.
+severity: high
+references:
+ - principle.product-model
+ - decision.feed-forward-over-review
+---
+
+Review docs copy for claims that Ghost review grades, approves, blocks, judges,
+or guarantees brand fit. Flag wording that treats checks as generation input or
+turns advisory review packets into CI gates.
+
+Accept wording that says checks are optional deterministic assertions, review
+assembles packets, host agents judge, and teams may choose to enforce separate
+CI around their own deterministic checks.
diff --git a/apps/docs/.ghost/condition.cli-reference-exactness.md b/apps/docs/.ghost/condition.cli-reference-exactness.md
new file mode 100644
index 00000000..ee83923d
--- /dev/null
+++ b/apps/docs/.ghost/condition.cli-reference-exactness.md
@@ -0,0 +1,26 @@
+---
+description: CLI exactness condition — gather when editing command docs, generated CLI help, examples, flags, exit codes, or install instructions.
+materials:
+ - apps/docs/src/content/docs/cli-reference.mdx
+ - apps/docs/src/generated/cli-manifest.json
+ - packages/ghost/src/commands/**
+ - scripts/dump-cli-help.mjs
+---
+
+This condition applies whenever docs mention command names, flags, exit codes,
+JSON shape, install commands, or generated help output.
+
+When it applies:
+
+- Treat the CLI as the source of truth. Check `ghost --help`, command-specific
+ help, or `apps/docs/src/generated/cli-manifest.json` before editing examples.
+- If a command or flag changed, run `pnpm dump:cli-help` so the docs manifest
+ matches the CLI.
+- Keep examples minimal and copy-pastable. Prefer one direct command over a
+ shell story with hidden setup.
+- Do not document speculative flags, adapter behavior, or workflow aliases that
+ are only in the skill. If it is not in the CLI, call it an agent workflow, not
+ a command.
+
+This condition is narrower than the general docs voice. It optimizes for exact
+mechanics over rhetorical smoothness.
diff --git a/apps/docs/.ghost/decision.feed-forward-over-review.md b/apps/docs/.ghost/decision.feed-forward-over-review.md
new file mode 100644
index 00000000..ff0feae8
--- /dev/null
+++ b/apps/docs/.ghost/decision.feed-forward-over-review.md
@@ -0,0 +1,31 @@
+---
+description: Tradeoff behind leading with gather/pull before review — gather when docs positioning could overemphasize drift, checks, gates, or scoring.
+materials:
+ - apps/docs/src/content/docs/getting-started.mdx
+ - apps/docs/src/content/docs/checks-and-review.mdx
+ - apps/docs/src/app/tools/scan/page.tsx
+ - apps/docs/src/app/tools/drift/page.tsx
+---
+
+Decision trace: Ghost could have led with review because drift detection is easy
+to understand. We choose feed-forward authoring and recall as the center because
+that is the product's real leverage: the agent should receive the decision before
+it builds.
+
+What review is good for:
+
+- It makes drift visible after a diff.
+- It routes changed files to material-backed nodes and checks.
+- It gives a host agent an advisory packet for critique.
+
+Why review does not lead:
+
+- If the right truth was never gathered, review catches the failure late.
+- Calling review a gate makes Ghost sound like it judges brand fit
+ deterministically, which it does not.
+- Teams adopt faster when the first win is one repeated decision written down and
+ reused before generation.
+
+The decision reverses only for a page whose explicit job is `ghost review`, the
+checks directory, or CI-style integration. Even there, repeat the boundary: review is
+feed-back and advisory; checks do not leak into generation context.
diff --git a/apps/docs/.ghost/exemplar.home-thesis.md b/apps/docs/.ghost/exemplar.home-thesis.md
new file mode 100644
index 00000000..4446c24b
--- /dev/null
+++ b/apps/docs/.ghost/exemplar.home-thesis.md
@@ -0,0 +1,30 @@
+---
+description: Annotated home-page thesis exemplar — match this form when writing high-level Ghost narrative or landing copy.
+materials:
+ - apps/docs/src/app/page.tsx
+ - apps/docs/src/components/docs/hero.tsx
+---
+
+The current home page is normative for argument shape, restraint, and visual
+pace.
+
+What to match:
+
+- **Tiny hero, huge word.** The hero does not explain the whole product. It says
+ "Ghost" at wordmark scale and gives one sentence of context. That restraint is
+ the brand move.
+- **Editorial thesis cadence.** The thesis unfolds in paragraphs, not a grid of
+ benefits. It trusts the reader to follow a serious idea.
+- **Mechanics over adjectives.** The copy names concrete mechanics — flat corpus,
+ `gather`, `pull`, `review`, Git approval — instead of claiming the product is
+ simple, powerful, or magical.
+- **Selective emphasis.** Foreground text highlights only the load-bearing model
+ phrase. Do not bold every important sentence.
+- **Haunted restraint.** The concentric circles are spectral and low-opacity;
+ they make the page feel like Ghost without becoming decoration the prose must
+ fight.
+
+Do not copy incidentals: the exact paragraph count, current CLI ordering, and the
+word "UI" in the opening line can change as the product positioning evolves.
+Preserve the structure: one stark sign, one serious argument, then routes into
+the docs.
diff --git a/apps/docs/.ghost/glossary.md b/apps/docs/.ghost/glossary.md
new file mode 100644
index 00000000..cad2d372
--- /dev/null
+++ b/apps/docs/.ghost/glossary.md
@@ -0,0 +1,53 @@
+---
+kinds:
+ - name: principle
+ - name: condition
+ - name: pattern
+ - name: exemplar
+ - name: anti-goal
+ - name: asset
+ - name: decision
+ - name: voice
+---
+
+# principle
+
+Durable stance, tradeoff, or invariant. Principles are always-on unless a
+narrower condition explicitly limits them; patterns may narrow principles, never
+loosen them.
+
+# condition
+
+Situational truth. Use only when the stated situation holds, and do not treat the
+condition as a destination bucket.
+
+# pattern
+
+Reusable page, component, motion, or content composition. A pattern says when it
+applies, what is bound, what is open, and which principle it refines.
+
+# exemplar
+
+Concrete reference for form, rhythm, quality, code shape, voice, or behavior.
+Match the annotated qualities; do not copy incidental details.
+
+# anti-goal
+
+Rejected default, neighboring style, generic generated pattern, or plausible
+wrong answer. Always-on like a principle, but phrased as the thing to steer away
+from.
+
+# asset
+
+Material truth about concrete tokens, components, type, motion, canvases, docs,
+copy, code, or files. `materials` locates; prose explains what the files mean.
+
+# decision
+
+Worked tradeoff trace: what was chosen, what was rejected, why, and when that
+choice would reverse.
+
+# voice
+
+Language and naming truth: the product vocabulary, cadence, metaphor, and copy
+boundaries that make the docs sound like Ghost rather than generic SaaS docs.
diff --git a/apps/docs/.ghost/index.md b/apps/docs/.ghost/index.md
new file mode 100644
index 00000000..ce3b50ba
--- /dev/null
+++ b/apps/docs/.ghost/index.md
@@ -0,0 +1,53 @@
+---
+description: Always read first for docs-site work — non-negotiables, coverage, and silence posture for the Ghost docs surface.
+materials:
+ - apps/docs/src/app/page.tsx
+ - apps/docs/src/content/docs/*.mdx
+ - apps/docs/src/components/docs/**
+---
+
+This fingerprint governs the Ghost docs site: the landing page, documentation
+index, tool landings, MDX docs pages, and the small components that carry them.
+It does not govern Vessel as a reference component registry; Vessel has its own
+fingerprint under `packages/vessel/.ghost/`. When the docs consume Vessel
+materials, this fingerprint decides the docs' product stance and Vessel decides
+the component-system contract.
+
+## Non-negotiables
+
+- Ghost is **brand context for agents**, not a component library, lifecycle
+ manager, design archive, or autonomous judge. Keep the docs centered on the
+ feed-forward act: the agent reads repo-local brand truth before it builds.
+- Preserve the flat corpus model: `.ghost/` is a package of prose nodes;
+ `manifest.yml`, `glossary.md`, and `checks/` are reserved; everything else is
+ a node whose id comes from its filename. No hierarchy, inheritance, graph, or
+ edge language.
+- The page should feel like a serious tool, not a SaaS explainer template:
+ monochrome, restrained, typographic, with precise motion and only one
+ memorable visual gesture at a time.
+- Concrete command names matter. Use `ghost init`, `ghost validate`,
+ `ghost gather`, `ghost pull`, `ghost pulse`, `ghost checks init`, and
+ `ghost review` exactly unless the CLI changes and the generated manifest has
+ been updated.
+- Do not let optional review language obscure the boundary: checks and review
+ are feed-back, advisory, and never generation input.
+
+## How to read the corpus
+
+Read `principle.product-model`, `principle.visual-composition`, and
+`voice.docs-language` for broad docs work. Pull `asset.docs-materials` before
+changing UI, tokens, routes, MDX layouts, or command examples. Pull
+`pattern.docs-index-card-grid` for card-index pages and `pattern.doc-article`
+for MDX documentation pages. Pull `anti-goal.generated-docs-site` before any new
+visual or marketing surface.
+
+The `decision.*` nodes explain tradeoffs; they are not permanent rules unless a
+principle or pattern repeats them. `exemplar.*` nodes show how an existing page
+pulls off the stance; match the annotated qualities, not every literal detail.
+
+## Silence posture
+
+When this fingerprint is silent, proceed provisionally from nearby docs pages and
+Vessel's token/component contract for routine implementation. Ask before
+changing product model vocabulary, introducing new visual metaphors, adding a new
+agent workflow, or making the docs sound more like marketing than instruction.
diff --git a/packages/ghost-ui/.ghost/manifest.yml b/apps/docs/.ghost/manifest.yml
similarity index 74%
rename from packages/ghost-ui/.ghost/manifest.yml
rename to apps/docs/.ghost/manifest.yml
index d7193e3e..22e030bc 100644
--- a/packages/ghost-ui/.ghost/manifest.yml
+++ b/apps/docs/.ghost/manifest.yml
@@ -1,2 +1,2 @@
schema: ghost.fingerprint-package/v1
-id: ghost-ui
+id: local
diff --git a/apps/docs/.ghost/pattern.doc-article.md b/apps/docs/.ghost/pattern.doc-article.md
new file mode 100644
index 00000000..7d0b52a0
--- /dev/null
+++ b/apps/docs/.ghost/pattern.doc-article.md
@@ -0,0 +1,39 @@
+---
+description: MDX documentation article structure — use for guide pages, conceptual docs, CLI docs, authoring docs, and checks/review docs.
+materials:
+ - apps/docs/src/content/docs/*.mdx
+ - apps/docs/src/components/docs/docs-page-layout.tsx
+ - apps/docs/src/components/docs/doc-prose.tsx
+ - apps/docs/src/mdx-components.tsx
+---
+
+This pattern applies when the surface teaches a workflow or model in MDX.
+
+**Bound:**
+
+- The page is organized as `DocSection` rows, not a freeform prose blob. Section
+ titles sit in the left column on large screens; the content column carries the
+ explanation.
+- Each section should answer one job: problem, action, model, validation, or
+ caveat. Do not make section titles decorative.
+- Code examples are short and runnable-looking. Prefer exact commands over
+ pseudo-code; if a command is illustrative, say so in prose.
+- Frontmatter carries the route-level promise: title, description, kicker,
+ section, order, slug. The visible body should satisfy that promise quickly.
+- `Callout` is reserved for adoption shortcuts, beta caveats, warnings, or
+ boundaries that change user behavior. It is not a decoration block.
+- Long conceptual paragraphs are allowed, but every abstract claim should be
+ anchored soon after with a filename, command, or node example.
+
+**Open:**
+
+- A page may start with a first-win shortcut when the full model is too heavy for
+ a new user.
+- Tables are appropriate for matrices, command summaries, and property
+ definitions; lists are better for workflow steps and invariants.
+- Exact section order may vary, but the path should move from user's pain to
+ concrete action before deep model explanation.
+
+**Refines:** `principle.product-model`, `principle.visual-composition`, and
+`voice.docs-language`. If an article explains the schema accurately but does not
+make the user's next agent instruction obvious, it has missed the docs product.
diff --git a/apps/docs/.ghost/pattern.docs-index-card-grid.md b/apps/docs/.ghost/pattern.docs-index-card-grid.md
new file mode 100644
index 00000000..13a73cd2
--- /dev/null
+++ b/apps/docs/.ghost/pattern.docs-index-card-grid.md
@@ -0,0 +1,39 @@
+---
+description: Docs and tool index card grids — use for /docs, /tools, and per-tool landing pages that route users to a small set of next reads.
+materials:
+ - apps/docs/src/app/docs/page.tsx
+ - apps/docs/src/app/tools/page.tsx
+ - apps/docs/src/app/tools/scan/page.tsx
+ - apps/docs/src/app/tools/drift/page.tsx
+ - apps/docs/src/components/docs/animated-page-header.tsx
+---
+
+This pattern applies when a page is a routing surface: it names a section of the
+product and offers a small set of next reads or tools.
+
+**Bound:**
+
+- Start with `AnimatedPageHeader`: kicker, blunt title, one sentence of routing
+ context, and the short horizontal rule.
+- Use a low-count grid. Two or three columns are enough; if the page needs more
+ than five cards, reconsider the information architecture before adding visual
+ density.
+- Cards stay quiet by default: border-card, card background, muted icon, compact
+ title, one short description. The hover state can darken the border and invert
+ a title underline, but it should not become a whole animated tile.
+- Card copy explains the job of the destination, not the feature in abstract.
+ Prefer "Emit the fingerprint menu with gather" over "Powerful context
+ discovery."
+- Icons are thin-line Lucide symbols at the existing sizes and stroke widths.
+ They support scanning; they are not illustrations.
+
+**Open:**
+
+- Grid cardinality and card ordering may change with the docs IA.
+- The exact icon can change when it makes the destination easier to recognize.
+- A tool page may use a tighter chip strip instead of large cards when the
+ choices are secondary.
+
+**Refines:** `principle.visual-composition` and `voice.docs-language`. If a
+proposed card grid needs bright accents, screenshots, badges, or paragraph-long
+blurbs to feel useful, the content structure is wrong.
diff --git a/apps/docs/.ghost/pattern.landing-thesis.md b/apps/docs/.ghost/pattern.landing-thesis.md
new file mode 100644
index 00000000..57fb5a8e
--- /dev/null
+++ b/apps/docs/.ghost/pattern.landing-thesis.md
@@ -0,0 +1,34 @@
+---
+description: Home-page thesis structure — use when editing the landing page, hero, thesis section, or product narrative above the docs index.
+materials:
+ - apps/docs/src/app/page.tsx
+ - apps/docs/src/components/docs/hero.tsx
+ - apps/docs/src/components/docs/cycloid-canvas.tsx
+---
+
+The home page is an argument, not a feature tour. It should make one memorable
+claim about why Ghost exists and then let the docs carry the details.
+
+**Bound:**
+
+- Hero: one wordmark-scale title, one restrained sentence. The ghost rings can
+ supply atmosphere; do not add a second hero illustration or product screenshot.
+- Thesis: centered longform column around `62ch`, muted body copy, with only a
+ few foreground highlights for key model words. The rhythm is editorial, not
+ sales-led.
+- The story moves from problem to model to loop to payoff: agents can assemble
+ UI, but not preserve brand truth; Ghost captures flat prose nodes; the agent
+ gathers and pulls them; review makes drift visible.
+- Lists should clarify the model. If a list reads like benefits marketing,
+ rewrite it as artifacts and mechanics.
+- End with a transfer claim: a brand truth that cannot be recalled or reviewed
+ against cannot be delegated.
+
+**Open:**
+
+- The exact thesis paragraphs may evolve with the product language.
+- A single additional visual system can be introduced only if it sharpens the
+ argument and displaces an existing gesture rather than stacking on top.
+
+**Refines:** `principle.product-model`, `principle.visual-composition`, and
+`voice.docs-language`.
diff --git a/apps/docs/.ghost/principle.product-model.md b/apps/docs/.ghost/principle.product-model.md
new file mode 100644
index 00000000..33d1ab02
--- /dev/null
+++ b/apps/docs/.ghost/principle.product-model.md
@@ -0,0 +1,34 @@
+---
+description: Core Ghost product model — gather for docs copy, IA, onboarding, diagrams, workflow explanations, or any statement of what Ghost is.
+materials:
+ - apps/docs/src/app/page.tsx
+ - apps/docs/src/content/docs/getting-started.mdx
+ - apps/docs/src/content/docs/fingerprint-authoring.mdx
+ - apps/docs/src/content/docs/checks-and-review.mdx
+ - packages/ghost/src/commands/**
+---
+
+Ghost is a small deterministic layer around an interpretive human-agent loop.
+The docs should keep that model crisp:
+
+- **Feed-forward first.** Ghost helps the agent read the right repo-local brand
+ truths before it builds. Review is useful, but it is the second half of the
+ loop, not the headline.
+- **BYOA, not an autonomous designer.** The host agent reads, selects, writes,
+ and judges. The CLI performs repeatable work: scaffold, validate, gather the
+ menu, pull selected nodes, summarize local events, and assemble advisory
+ review packets.
+- **Flat node corpus.** A fingerprint is a flat `.ghost/` package of markdown
+ prose nodes. Kinds come from filename prefixes declared in `glossary.md`.
+ Altitude lives in prose; narrower truths name their condition. Do not describe
+ folders, inheritance, edge traversal, or schema fields as the conceptual model.
+- **Prose is executable context only through use.** A node steers because an
+ agent can find it, read it, and manifest it. Keep retrieval handles and command
+ examples close to abstract claims.
+- **Git is the approval boundary.** Uncommitted fingerprint edits are drafts;
+ checked-in node prose is canonical through normal review.
+
+When the docs must choose between theoretical completeness and a simple first
+win, the first win wins. The canonical onboarding story is: write the decision
+you keep repeating once, put it in `.ghost/`, gather it before generation, and
+review drift after the diff.
diff --git a/apps/docs/.ghost/principle.visual-composition.md b/apps/docs/.ghost/principle.visual-composition.md
new file mode 100644
index 00000000..39b52795
--- /dev/null
+++ b/apps/docs/.ghost/principle.visual-composition.md
@@ -0,0 +1,38 @@
+---
+description: Visual composition floor for the docs site — gather before changing landing pages, headers, cards, navigation, motion, or layout rhythm.
+materials:
+ - apps/docs/src/app/page.tsx
+ - apps/docs/src/app/docs/page.tsx
+ - apps/docs/src/app/tools/**/*.tsx
+ - apps/docs/src/components/docs/**
+ - packages/vessel/src/styles/main.css
+---
+
+The Ghost docs site should render like a stark technical artifact with one
+controlled spectral move, not a generic docs template.
+
+Hard floor:
+
+- **Monochrome carries authority.** Default to background, foreground, muted,
+ border, and card roles. Color is rare and semantic; never invent a product
+ accent for excitement.
+- **Typography is the hero.** Large display type, tight tracking, uppercase
+ kickers, and generous line-height do most of the branding. If a page needs
+ impact, increase type hierarchy or negative space before adding illustration.
+- **One memorable visual gesture per surface.** The landing page can have the
+ concentric ghost rings. A docs index can have card underlines. A prose page can
+ have sticky section labels. Do not stack rings, gradients, decorative blobs,
+ screenshots, and animated cards on the same surface.
+- **Flat before elevated.** Reach for whitespace, alignment, rule lines, and
+ border-card before shadows or heavy containers. The floating dock is the main
+ elevated object; new elevation must earn its presence.
+- **Motion reveals structure.** Use motion to stage reading order or clarify a
+ state change. Avoid ambient decoration, bounce, springiness, or motion that
+ competes with the text.
+- **Density is editorial.** Docs pages should feel sparse at the frame level but
+ precise inside the prose. Long text belongs in the MDX article system; landing
+ pages should have short thesis blocks and strong scannable routes.
+
+When a pattern conflicts with this floor, the floor wins and the pattern is
+wrong. When Vessel tokens make a choice available but this floor rejects the
+output, choose the docs fingerprint.
diff --git a/apps/docs/.ghost/voice.docs-language.md b/apps/docs/.ghost/voice.docs-language.md
new file mode 100644
index 00000000..acf947b9
--- /dev/null
+++ b/apps/docs/.ghost/voice.docs-language.md
@@ -0,0 +1,41 @@
+---
+description: Ghost docs voice and vocabulary — gather before writing docs copy, headings, onboarding text, CLI examples, or product explanations.
+materials:
+ - apps/docs/src/content/docs/*.mdx
+ - apps/docs/src/app/page.tsx
+ - apps/docs/src/app/docs/page.tsx
+ - apps/docs/src/app/tools/**/*.tsx
+---
+
+Ghost docs should sound like an opinionated tool builder explaining a sharp
+model, not like a launch page selling a platform.
+
+Voice rules:
+
+- Lead with the user's repeated pain: the same review comment, the same drift,
+ the same missing decision. Then show the small move Ghost makes possible.
+- Prefer short declarative sentences around the model: "The CLI does the
+ deterministic work; your agent does the interpretation." Do not bury the
+ boundary in hedging.
+- Use concrete artifact names: `.ghost/`, `manifest.yml`, `glossary.md`,
+ `index.md`, `checks/`, `ghost gather`, `ghost pull`. The docs earn trust by
+ naming the files and commands agents actually touch.
+- Say **brand truth**, **fingerprint**, **node**, **kind**, **materials**,
+ **feed-forward**, **feed-back**, and **advisory review packet**
+ consistently. Do not introduce synonyms such as brand DNA, style cache,
+ design brain, magic layer, or guardrail engine.
+- Explain by contrast: feed-forward versus feed-back, deterministic CLI versus
+ interpretive agent, flat corpus versus traversal, testimony versus truth,
+ draft versus canonical.
+- Keep the claim plain when the idea is abstract. A good Ghost sentence should
+ make a wrong implementation less likely, not merely sound clever.
+
+Copy anti-patterns:
+
+- No "unlock," "supercharge," "seamless," "delightful," "AI-powered," or
+ "world-class" filler.
+- No exclamation points in instructional copy.
+- No anthropomorphizing Ghost as the judge or designer. The agent judges; Ghost
+ assembles context and packets.
+- No broad promises that Ghost preserves a whole brand automatically. It only
+ preserves what the fingerprint makes selectable, readable, and reviewable.
diff --git a/apps/docs/README.md b/apps/docs/README.md
index 617caf02..c3a3d0c4 100644
--- a/apps/docs/README.md
+++ b/apps/docs/README.md
@@ -2,7 +2,7 @@
**Documentation site for the Ghost project.**
-`ghost-docs` is the deployed docs for everything in this monorepo: the `ghost` CLI, the fingerprint package format, the generation loop, and the live `ghost-ui` component catalogue. A Vite + MDX app that consumes [`ghost-ui`](../../packages/ghost-ui) as a workspace dependency.
+`ghost-docs` is the deployed docs for everything in this monorepo: the `ghost` CLI, the fingerprint package format, the generation loop, and the live `vessel` component catalogue. A Vite + MDX app that consumes [`vessel`](../../packages/vessel) as a workspace dependency.
## Run
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 56ea5b35..ffbca9dd 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -10,7 +10,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "ghost-ui": "workspace:*",
+ "@design-intelligence/vessel": "workspace:*",
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
diff --git a/apps/docs/src/App.tsx b/apps/docs/src/App.tsx
index cb3515a5..23bc35da 100644
--- a/apps/docs/src/App.tsx
+++ b/apps/docs/src/App.tsx
@@ -1,27 +1,14 @@
-import { ThemeProvider } from "ghost-ui";
+import { ThemeProvider } from "@design-intelligence/vessel";
import { useEffect } from "react";
-import { Navigate, Route, Routes, useLocation, useParams } from "react-router";
+import { Navigate, Route, Routes, useLocation } from "react-router";
import DocsIndex from "@/app/docs/page";
import HomePage from "@/app/page";
import GhostDriftLanding from "@/app/tools/drift/page";
-import GhostFleetLanding from "@/app/tools/fleet/page";
import ToolsIndex from "@/app/tools/page";
import GhostScanLanding from "@/app/tools/scan/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";
-import FoundationsIndex from "@/app/ui/foundations/page";
-import TypographyPage from "@/app/ui/foundations/typography/page";
-import DesignLanguageIndex from "@/app/ui/page";
import { Dock } from "@/components/docs/dock";
import { mdxDocsRoutes } from "@/routes/docs-routes";
-function ComponentRedirect() {
- const { name } = useParams<{ name: string }>();
- return ;
-}
-
function ScrollToHash() {
const { hash, pathname } = useLocation();
@@ -51,7 +38,7 @@ export function App() {
} />
- {/* Tools — four-card index plus per-tool landings */}
+ {/* Tools: four-card index plus per-tool landings */}
} />
} />
} />
- } />
- } />
{/* Cross-tool docs hub */}
} />
@@ -74,17 +59,6 @@ export function App() {
{/* MDX-authored doc pages under /docs/* */}
{mdxDocsRoutes()}
- {/* Design Language (ghost-ui catalogue) */}
- } />
- } />
- } />
- }
- />
- } />
- } />
-
{/* Redirects from the previous /tools/drift/{getting-started,cli} URLs */}
}
/>
-
- {/* Redirects from legacy root /foundations and /components URLs */}
- }
- />
- }
- />
- }
- />
- }
- />
- } />
diff --git a/apps/docs/src/app/docs/page.tsx b/apps/docs/src/app/docs/page.tsx
index 2bf96ca0..78624df5 100644
--- a/apps/docs/src/app/docs/page.tsx
+++ b/apps/docs/src/app/docs/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import { useStaggerReveal } from "ghost-ui";
-import { BookOpen, Rocket } from "lucide-react";
+import { useStaggerReveal } from "@design-intelligence/vessel";
+import { BookOpen, FileText, Rocket, ShieldCheck, Zap } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router";
import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
@@ -13,6 +13,13 @@ const sections: {
description: string;
icon: ReactNode;
}[] = [
+ {
+ name: "Five-Minute Ghost",
+ href: "/docs/quickstart",
+ description:
+ "Write down the one decision you keep repeating, once, and let your agent read it before it builds.",
+ icon: ,
+ },
{
name: "Getting Started",
href: "/docs/getting-started",
@@ -20,11 +27,25 @@ const sections: {
"Install Ghost, set up the repo fingerprint, and learn the loop around .ghost.",
icon: ,
},
+ {
+ name: "Fingerprint Authoring",
+ href: "/docs/fingerprint-authoring",
+ description:
+ "Co-author flat prose nodes that steer generation, with kinds from the glossary.",
+ icon: ,
+ },
+ {
+ name: "Checks And Review",
+ href: "/docs/checks-and-review",
+ description:
+ "Opt in to review checks, bind review assertions to nodes, and assemble advisory packets from a diff.",
+ icon: ,
+ },
{
name: "CLI Reference",
href: "/docs/cli",
description:
- "Commands for checks and comparison, plus the skill recipes your agent runs.",
+ "Every command around the flat fingerprint: init, checks, validate, gather, pull, pulse, and review.",
icon: ,
},
];
diff --git a/apps/docs/src/app/page.tsx b/apps/docs/src/app/page.tsx
index 12e77918..67d599df 100644
--- a/apps/docs/src/app/page.tsx
+++ b/apps/docs/src/app/page.tsx
@@ -1,4 +1,4 @@
-import { useStaggerReveal } from "ghost-ui";
+import { useStaggerReveal } from "@design-intelligence/vessel";
import { Hero } from "@/components/docs/hero";
import { SectionWrapper } from "@/components/docs/wrappers";
@@ -29,121 +29,52 @@ export default function Home() {
- Agents can assemble UI. What they cannot reliably preserve is the
- surface composition that UI belongs to.
+ Brand is not the logo. {" "}
+ It's the accumulated stance behind everything you ship: what
+ you say plainly, where you slow down, what you refuse to do, how
+ you behave at the moment someone trusts you. It flows through
+ every surface — the screen, the email, the empty state, the
+ sentence.
- For years, design systems solved a human assembly problem. They
- gave teams shared tokens, components, examples, and usage rules so
- new surfaces could be composed from known parts.
+ Agents now do the making. {" "}
+ They're fast, they're capable, and they hold nothing
+ they aren't handed. Every generated screen and sentence is an
+ answer to a question the agent was never asked:{" "}
+
+ what would this brand do here?
+
- That layer still matters, but agents change the scarce layer.
- Models can copy local patterns and recombine components. They do
- not consistently preserve the composition that makes a product
- surface feel intentional: hierarchy, density, restraint, behavior,
- copy, accessibility, trust, and flow.
+
+ Ghost writes the answer down, where the making happens.
+ {" "}
+ The fingerprint is a portable steering packet: plain prose truths,
+ one file each, checked into the repo. A truth is stated once, at
+ the altitude it is actually true — “near the moment of
+ payment, reduce felt risk” — and steers whatever is being
+ made from it. Any agent reads the same packet: Claude, Codex,
+ Cursor, Goose. Any medium takes the same steer: a screen, a page,
+ a sentence. It travels with the repo, and it outlives your choice
+ of agent.
- Ghost captures the composition of a product surface: the intent
- behind it, the materials it draws from, and the patterns that make
- it feel intentional.
+
+ Around the packet, machinery — and only machinery.
+ {" "}
+ ghost gather emits the menu of truths;{" "}
+ ghost pull delivers the ones that fit the task;{" "}
+ ghost review assembles an advisory packet after the
+ work, never during. The CLI computes and never decides. The packet
+ is the product; the CLI is the courier.
- It stores that composition as checked-in fingerprint facets: which
- intent shapes the surface, which materials agents can draw from,
- which situations change the obligation, which patterns hold the
- surface together, and which examples show it at its best.
-
-
- Components, tokens, and libraries become implementation material.
- Ghost does not replace them. It gives agents the surface context
- that tells them when and how those materials belong.
-
-
Ghost keeps that model compact:
-
-
- .ghost/ is the default portable fingerprint package
-
-
- intent.yml, inventory.yml, and{" "}
- composition.yml store the three facets
-
-
- validate.yml stores optional deterministic gates
- grounded in fingerprint refs
-
-
- ordinary Git review separates draft fingerprint edits from
- checked-in truth
-
-
-
- The split is deliberate. intent.yml captures the
- intent behind the surface. inventory.yml captures the
- materials it draws from. composition.yml captures the
- patterns that make it feel intentional. Checks validate output;
- they are not generation input.
-
-
A typical loop becomes:
-
- Brief from the fingerprint facets and exemplars
- Generate or edit with the host agent
- Run active deterministic checks and advisory review
-
- Fix code, explain intentional divergence, or update the Ghost
- package through Git
-
-
-
- Ghost stays bring-your-own-agent. The agent reads, decides, and
- writes. Ghost does the repeatable work: initialization, schema
- validation, inventory, evidence verification, checks, advisory
- review packets, comparison, and upstream handoff packets.
-
-
- This is critical because surface composition that cannot be
- recalled or evaluated cannot be delegated. A product surface that
- only its original author can assess is not transferable: to
- agents, to new engineers, or to forks of the product.
-
-
- Drift becomes measurable within this system. When generated or
- modified UI diverges from checked-in fingerprint facets, the
- failure is not just error; it is signal. Drift can originate from:
-
-
- incorrect generation: agent failure
- missing-fingerprint: under-specified surface context
- intentional product evolution
-
-
- Ghost does not eliminate drift; it surfaces and localizes it. The
- system's boundary becomes visible where composition fails.
-
-
- The fingerprint package must live where generation happens: in the
- repository, versioned alongside the code it governs. As the
- product changes, fingerprint edits move through the same ordinary
- Git review that introduces new UI.
-
-
- This leads to a practical governance model. Each repository owns
- its product-surface fingerprint. Advanced workflows can add nested
- packages for product areas, custom fingerprint directories for
- host wrappers, comparison across systems, and declared drift
- stances.
-
-
- Across an organization, the collection of Ghost packages forms a
- higher-order map: a distributed model of product-surface
- composition as it is actually practiced, not as it is only
- described.
-
-
- Design systems were libraries for humans. Ghost is composition
- context for agents: every surface can carry the fingerprint it
- extends, and every deviation can carry evidence.
+
+ Brand used to survive by being remembered
+ {" "}
+ — carried in heads, enforced one review comment at a time. Ghost
+ makes it something stronger: written once, read before anything is
+ made, carried by everything that ships.
diff --git a/apps/docs/src/app/tools/drift/page.tsx b/apps/docs/src/app/tools/drift/page.tsx
index 80cb40c5..98bfda0e 100644
--- a/apps/docs/src/app/tools/drift/page.tsx
+++ b/apps/docs/src/app/tools/drift/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useStaggerReveal } from "ghost-ui";
+import { useStaggerReveal } from "@design-intelligence/vessel";
import { BookOpen, Orbit, Rocket } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router";
@@ -14,24 +14,24 @@ const cards: {
icon: ReactNode;
}[] = [
{
- name: "Ghost loop",
- href: "/docs/getting-started#the-simple-model",
+ name: "Checks and review",
+ href: "/docs/checks-and-review",
description:
- "See how context, checks, review, comparison, and intent fit together.",
+ "Opt in to review checks, bind assertions to nodes, and assemble advisory packets.",
icon: ,
},
{
name: "Get started",
href: "/docs/getting-started",
description:
- "Install the skill bundle and review changed UI against the .ghost bundle.",
+ "Install the skill bundle and review changed work against the .ghost fingerprint.",
icon: ,
},
{
name: "CLI reference",
- href: "/docs/cli#ghost--review-and-compare",
+ href: "/docs/cli",
description:
- "Run checks, emit exemplar-guided advisory review, compare fingerprints, and record intent.",
+ "Every command around the flat fingerprint, including review flags and exit codes.",
icon: ,
},
];
@@ -47,8 +47,8 @@ export default function GhostDriftLanding() {
,
- },
- {
- name: "CLI reference",
- href: "/docs/cli#ghost-fleet--view-across-projects",
- description: "List members, compute a view, and emit fleet reports.",
- icon: ,
- },
- {
- name: "Skill bundle",
- href: "https://github.com/block/ghost/tree/main/packages/ghost-fleet/src/skill-bundle",
- description: "The recipe that turns fleet output into a readable summary.",
- icon: ,
- },
-];
-
-export default function GhostFleetLanding() {
- const ref = useStaggerReveal(".tool-card", {
- stagger: 0.06,
- y: 30,
- duration: 0.7,
- });
-
- return (
-
-
-
-
- {cards.map((item) => (
-
-
- {item.icon}
-
-
-
- {item.name}
-
-
-
-
- {item.description}
-
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/app/tools/page.tsx b/apps/docs/src/app/tools/page.tsx
index ffaefd8f..f6844569 100644
--- a/apps/docs/src/app/tools/page.tsx
+++ b/apps/docs/src/app/tools/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import { useStaggerReveal } from "ghost-ui";
-import { FileText, Network, Orbit, Palette } from "lucide-react";
+import { useStaggerReveal } from "@design-intelligence/vessel";
+import { FileText, Orbit } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router";
import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
@@ -14,29 +14,17 @@ const tools: {
icon: ReactNode;
}[] = [
{
- name: "ghost scan",
+ name: "ghost gather",
href: "/tools/scan",
- blurb: "Check fingerprint contribution",
+ blurb: "Gather brand context before building",
icon: ,
},
{
name: "ghost review",
href: "/tools/drift",
- blurb: "Review UI drift",
+ blurb: "Review a diff against the fingerprint",
icon: ,
},
- {
- name: "ghost-fleet",
- href: "/tools/fleet",
- blurb: "Compare projects",
- icon: ,
- },
- {
- name: "ghost-ui",
- href: "/tools/ui",
- blurb: "Reference UI library",
- icon: ,
- },
];
function ToolStrip() {
@@ -49,7 +37,7 @@ function ToolStrip() {
return (
{tools.map((tool) => (
diff --git a/apps/docs/src/app/tools/scan/page.tsx b/apps/docs/src/app/tools/scan/page.tsx
index c4a97f49..eaf2debf 100644
--- a/apps/docs/src/app/tools/scan/page.tsx
+++ b/apps/docs/src/app/tools/scan/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useStaggerReveal } from "ghost-ui";
+import { useStaggerReveal } from "@design-intelligence/vessel";
import { BookOpen, FileText, Rocket } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router";
@@ -22,16 +22,15 @@ const cards: {
},
{
name: "CLI reference",
- href: "/docs/cli#ghost--fingerprint-layers-and-package-checks",
+ href: "/docs/cli",
description:
- "Check fingerprint contribution facets, validate packages, and emit context.",
+ "Emit the fingerprint menu with gather, read selected truths with pull, and tune with pulse.",
icon:
,
},
{
- name: "Format spec",
- href: "https://github.com/block/ghost/blob/main/docs/fingerprint-format.md",
- description:
- "The full package format for fingerprint intent, inventory, composition, and validation.",
+ name: "Authoring",
+ href: "/docs/fingerprint-authoring",
+ description: "How to write node prose that steers generation.",
icon:
,
},
];
@@ -46,9 +45,9 @@ export default function GhostScanLanding() {
return (
,
- },
- {
- name: `Components (${componentCount})`,
- href: "/ui/components",
- description:
- "Production-ready primitives + AI elements. Distributed via the shadcn registry.json — installed component-by-component, never wholesale.",
- icon: ,
- },
- {
- name: "MCP server",
- href: "https://github.com/block/ghost/tree/main/packages/ghost-ui#mcp-server",
- description:
- "ghost-mcp re-exposes the registry to AI assistants — five tools, two resources, so an agent can search components and pull source.",
- icon: ,
- },
-];
-
-export default function GhostUiLanding() {
- const ref = useStaggerReveal(".tool-card", {
- stagger: 0.06,
- y: 30,
- duration: 0.7,
- });
-
- return (
-
-
-
-
- {cards.map((item) => (
-
-
- {item.icon}
-
-
-
- {item.name}
-
-
-
-
- {item.description}
-
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/app/ui/components/[name]/page.tsx b/apps/docs/src/app/ui/components/[name]/page.tsx
deleted file mode 100644
index 4291697f..00000000
--- a/apps/docs/src/app/ui/components/[name]/page.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Navigate, useParams } from "react-router";
-import { ComponentPageShell } from "@/components/docs/component-page-shell";
-import { getComponentDoc } from "@/lib/component-docs";
-import {
- getCategory,
- getComponent,
- getComponentsByCategory,
-} from "@/lib/component-registry";
-import { getComponentSpec } from "@/lib/component-source";
-
-// ── Import demo source files as raw strings at build time ──
-
-const demoSourceModules = import.meta.glob(
- [
- "/src/components/docs/primitives/*-demo.tsx",
- "/src/components/docs/ai-elements/*-demo.tsx",
- ],
- { query: "?raw", eager: true },
-) as Record;
-
-function getDemoSource(
- slug: string,
- source: "primitives" | "ai-elements",
-): string | null {
- const key = `/src/components/docs/${source}/${slug}-demo.tsx`;
- return demoSourceModules[key]?.default ?? null;
-}
-
-export default function ComponentPage() {
- const { name } = useParams<{ name: string }>();
-
- if (!name) return ;
-
- const component = getComponent(name);
- if (!component) return ;
-
- const category = getCategory(component.primaryCategory);
- const siblings = getComponentsByCategory(component.primaryCategory);
- const currentIndex = siblings.findIndex((c) => c.slug === name);
- const prev = currentIndex > 0 ? siblings[currentIndex - 1] : null;
- const next =
- currentIndex < siblings.length - 1 ? siblings[currentIndex + 1] : null;
-
- const demoSource = getDemoSource(component.slug, component.demoSource);
- const spec = getComponentSpec(component.slug);
- const docs = getComponentDoc(name);
-
- return (
-
- );
-}
diff --git a/apps/docs/src/app/ui/components/page.tsx b/apps/docs/src/app/ui/components/page.tsx
deleted file mode 100644
index 5be57442..00000000
--- a/apps/docs/src/app/ui/components/page.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-"use client";
-
-import { useStaggerReveal } from "ghost-ui";
-import { useMemo, useState } from "react";
-import { Link } from "react-router";
-import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
-import { SectionWrapper } from "@/components/docs/wrappers";
-import {
- categories,
- getAllComponents,
- getComponentsByCategory,
-} from "@/lib/component-registry";
-
-/* ── Fuzzy match ─────────────────────────────────────────────────────── */
-
-function fuzzyMatch(query: string, target: string): number {
- const q = query.toLowerCase();
- const t = target.toLowerCase();
-
- // exact substring match scores highest
- if (t.includes(q)) return 1;
-
- // character-by-character fuzzy: every query char must appear in order
- let qi = 0;
- let score = 0;
- let lastIdx = -1;
-
- for (let ti = 0; ti < t.length && qi < q.length; ti++) {
- if (t[ti] === q[qi]) {
- // bonus for consecutive matches
- score += ti === lastIdx + 1 ? 2 : 1;
- lastIdx = ti;
- qi++;
- }
- }
-
- // all query characters must be found
- if (qi < q.length) return 0;
-
- // normalise to 0–1 range (below 1 so substring match always wins)
- return (score / (q.length * 2)) * 0.9;
-}
-
-/* ── Page ─────────────────────────────────────────────────────────────── */
-
-export default function ComponentsIndex() {
- const [query, setQuery] = useState("");
- const allComponents = useMemo(() => getAllComponents(), []);
-
- const filtered = useMemo(() => {
- if (!query.trim()) return null;
- return allComponents
- .map((c) => ({ ...c, score: fuzzyMatch(query, c.name) }))
- .filter((c) => c.score > 0)
- .sort((a, b) => b.score - a.score);
- }, [query, allComponents]);
-
- const isSearching = query.trim().length > 0;
-
- return (
-
-
-
- {/* Search */}
-
- setQuery(e.target.value)}
- placeholder="Search components…"
- className="w-full max-w-md rounded-full border border-border-card bg-card px-5 py-2.5 text-sm text-foreground placeholder:text-muted-foreground/50 outline-none focus:border-foreground/25 transition-colors duration-200"
- />
-
-
- {/* Search results */}
- {isSearching && (
-
- {filtered && filtered.length > 0 ? (
-
- {filtered.map((item) => (
-
- ))}
-
- ) : (
-
- No components match "{query}"
-
- )}
-
- )}
-
- {/* Category sections */}
- {!isSearching && (
-
- {categories.map((cat) => {
- const items = getComponentsByCategory(cat.slug);
- if (items.length === 0) return null;
- return (
-
- );
- })}
-
- )}
-
- );
-}
-
-/* ── Pill ─────────────────────────────────────────────────────────────── */
-
-function ComponentPill({ slug, name }: { slug: string; name: string }) {
- return (
-
-
-
- {name}
-
-
- );
-}
-
-/* ── Category section ─────────────────────────────────────────────────── */
-
-function CategorySection({
- name,
- description,
- items,
-}: {
- name: string;
- description: string;
- items: { slug: string; name: string }[];
-}) {
- const ref = useStaggerReveal(".component-card", {
- stagger: 0.04,
- y: 24,
- duration: 0.6,
- });
-
- return (
-
-
- {name}
-
-
{description}
-
- {items.map((item) => (
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/app/ui/foundations/colors/page.tsx b/apps/docs/src/app/ui/foundations/colors/page.tsx
deleted file mode 100644
index ddaa3355..00000000
--- a/apps/docs/src/app/ui/foundations/colors/page.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-"use client";
-
-import { useScrollReveal } from "ghost-ui";
-import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
-import { ColorsDemos } from "@/components/docs/foundations/colors";
-import { SectionWrapper } from "@/components/docs/wrappers";
-
-export default function ColorsPage() {
- const contentRef = useScrollReveal({
- y: 50,
- duration: 0.9,
- ease: "expo.out",
- });
-
- return (
- <>
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/apps/docs/src/app/ui/foundations/page.tsx b/apps/docs/src/app/ui/foundations/page.tsx
deleted file mode 100644
index 1f648342..00000000
--- a/apps/docs/src/app/ui/foundations/page.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-"use client";
-
-import { useStaggerReveal } from "ghost-ui";
-import { type ReactNode } from "react";
-import { Link } from "react-router";
-import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
-import { SectionWrapper } from "@/components/docs/wrappers";
-
-function ColorsVisual() {
- return (
-
- {[
- "bg-foreground",
- "bg-foreground/80",
- "bg-foreground/60",
- "bg-foreground/40",
- "bg-foreground/20",
- "bg-foreground/10",
- ].map((bg, i) => (
-
- ))}
-
- );
-}
-
-function TypographyVisual() {
- return (
-
- );
-}
-
-const foundations: {
- name: string;
- href: string;
- description: string;
- visual: ReactNode;
-}[] = [
- {
- name: "Colors",
- href: "/ui/foundations/colors",
- description:
- "A pure monochromatic scale with selective semantic color for status and utility.",
- visual: ,
- },
- {
- name: "Typography",
- href: "/ui/foundations/typography",
- description:
- "Magazine-grade hierarchy. Display for headers, Regular for body, Mono for data.",
- visual: ,
- },
-];
-
-export default function FoundationsIndex() {
- const ref = useStaggerReveal(".foundation-card", {
- stagger: 0.06,
- y: 30,
- duration: 0.7,
- });
-
- return (
-
-
-
-
- {foundations.map((item) => (
-
-
{item.visual}
-
-
- {item.name}
-
-
-
-
- {item.description}
-
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/app/ui/foundations/typography/page.tsx b/apps/docs/src/app/ui/foundations/typography/page.tsx
deleted file mode 100644
index d6a8f53c..00000000
--- a/apps/docs/src/app/ui/foundations/typography/page.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-"use client";
-
-import { useScrollReveal } from "ghost-ui";
-import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
-import { TypographyDemos } from "@/components/docs/foundations/typography";
-import { SectionWrapper } from "@/components/docs/wrappers";
-
-export default function TypographyPage() {
- const contentRef = useScrollReveal({
- y: 50,
- duration: 0.9,
- ease: "expo.out",
- });
-
- return (
- <>
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/apps/docs/src/app/ui/page.tsx b/apps/docs/src/app/ui/page.tsx
deleted file mode 100644
index 686405ef..00000000
--- a/apps/docs/src/app/ui/page.tsx
+++ /dev/null
@@ -1,131 +0,0 @@
-"use client";
-
-import { useStaggerReveal } from "ghost-ui";
-import { type ReactNode } from "react";
-import { Link } from "react-router";
-import { AnimatedPageHeader } from "@/components/docs/animated-page-header";
-import { SectionWrapper } from "@/components/docs/wrappers";
-import { getAllComponents } from "@/lib/component-registry";
-
-function ColorsVisual() {
- return (
-
- {[
- "bg-foreground",
- "bg-foreground/80",
- "bg-foreground/60",
- "bg-foreground/40",
- "bg-foreground/20",
- "bg-foreground/10",
- ].map((bg, i) => (
-
- ))}
-
- );
-}
-
-function TypographyVisual() {
- return (
-
- );
-}
-
-function ComponentsVisual() {
- const count = getAllComponents().length;
- return (
-
- {Array.from({ length: 8 }).map((_, i) => (
-
- ))}
-
- {count} components
-
-
- );
-}
-
-const sections: {
- name: string;
- href: string;
- description: string;
- visual: ReactNode;
-}[] = [
- {
- name: "Foundations",
- href: "/ui/foundations",
- description:
- "Color, typography, and the design tokens that underpin every Ghost UI component.",
- visual: (
-
- ),
- },
- {
- name: "Components",
- href: "/ui/components",
- description:
- "Production-ready building blocks. Every component follows Ghost UI — pill-first, monochromatic, accessible.",
- visual: ,
- },
-];
-
-export default function DesignLanguageIndex() {
- const ref = useStaggerReveal(".dl-card", {
- stagger: 0.06,
- y: 30,
- duration: 0.7,
- });
-
- return (
-
-
-
-
- {sections.map((item) => (
-
-
{item.visual}
-
-
- {item.name}
-
-
-
-
- {item.description}
-
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/agent-demo.tsx b/apps/docs/src/components/docs/ai-elements/agent-demo.tsx
deleted file mode 100644
index 0b340865..00000000
--- a/apps/docs/src/components/docs/ai-elements/agent-demo.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-"use client";
-
-import {
- Agent,
- AgentContent,
- AgentHeader,
- AgentInstructions,
- AgentOutput,
- AgentTool,
- AgentTools,
-} from "ghost-ui";
-
-export function AgentDemo() {
- return (
-
-
-
-
-
- You are a research assistant that helps users find and summarize
- academic papers. Use the provided tools to search databases and
- retrieve relevant publications. Always cite your sources.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Review code for best practices, potential bugs, and performance
- issues. Provide actionable feedback with specific line references.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/artifact-demo.tsx b/apps/docs/src/components/docs/ai-elements/artifact-demo.tsx
deleted file mode 100644
index ab5105a5..00000000
--- a/apps/docs/src/components/docs/ai-elements/artifact-demo.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-"use client";
-
-import {
- Artifact,
- ArtifactAction,
- ArtifactActions,
- ArtifactClose,
- ArtifactContent,
- ArtifactDescription,
- ArtifactHeader,
- ArtifactTitle,
-} from "ghost-ui";
-import { CopyIcon, DownloadIcon, ShareIcon } from "lucide-react";
-
-export function ArtifactDemo() {
- return (
-
-
-
-
-
React Component
-
- A reusable button component with variants
-
-
-
-
-
-
-
-
-
-
-
- {`export function Button({ variant = "primary", children }) {
- return (
-
- {children}
-
- );
-}`}
-
-
-
-
-
-
-
-
SVG Illustration
-
- Generated logo design concept
-
-
-
-
-
-
-
-
-
- AI
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/attachments-demo.tsx b/apps/docs/src/components/docs/ai-elements/attachments-demo.tsx
deleted file mode 100644
index a5caa72c..00000000
--- a/apps/docs/src/components/docs/ai-elements/attachments-demo.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-"use client";
-
-import {
- Attachment,
- AttachmentEmpty,
- AttachmentInfo,
- AttachmentPreview,
- AttachmentRemove,
- Attachments,
-} from "ghost-ui";
-
-const mockAttachments = [
- {
- id: "1",
- type: "file" as const,
- mediaType: "image/png",
- filename: "screenshot.png",
- url: "https://picsum.photos/seed/attach1/200/200",
- },
- {
- id: "2",
- type: "file" as const,
- mediaType: "application/pdf",
- filename: "quarterly-report.pdf",
- url: "",
- },
- {
- id: "3",
- type: "file" as const,
- mediaType: "audio/mp3",
- filename: "recording.mp3",
- url: "",
- },
-];
-
-export function AttachmentsDemo() {
- return (
-
-
-
Grid variant
-
- {mockAttachments.map((file) => (
- {}}>
-
-
-
- ))}
-
-
-
-
-
Inline variant
-
- {mockAttachments.map((file) => (
- {}}>
-
-
-
-
- ))}
-
-
-
-
-
List variant
-
- {mockAttachments.map((file) => (
- {}}>
-
-
-
-
- ))}
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/audio-player-demo.tsx b/apps/docs/src/components/docs/ai-elements/audio-player-demo.tsx
deleted file mode 100644
index eb65a58e..00000000
--- a/apps/docs/src/components/docs/ai-elements/audio-player-demo.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-"use client";
-
-import {
- AudioPlayer,
- AudioPlayerControlBar,
- AudioPlayerDurationDisplay,
- AudioPlayerElement,
- AudioPlayerMuteButton,
- AudioPlayerPlayButton,
- AudioPlayerSeekBackwardButton,
- AudioPlayerSeekForwardButton,
- AudioPlayerTimeDisplay,
- AudioPlayerTimeRange,
- AudioPlayerVolumeRange,
-} from "ghost-ui";
-
-export function AudioPlayerDemo() {
- return (
-
-
-
Full audio player
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Minimal player (play, time, scrub)
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/canvas-demo.tsx b/apps/docs/src/components/docs/ai-elements/canvas-demo.tsx
deleted file mode 100644
index 27fc23c1..00000000
--- a/apps/docs/src/components/docs/ai-elements/canvas-demo.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-"use client";
-
-import { type NodeTypes, ReactFlowProvider } from "@xyflow/react";
-import {
- Canvas,
- Controls,
- Node,
- NodeContent,
- NodeDescription,
- NodeHeader,
- NodeTitle,
-} from "ghost-ui";
-import { useMemo } from "react";
-
-const InputNode = () => (
-
-
- User Input
- Text prompt
-
-
-
- Accepts a natural language query from the user.
-
-
-
-);
-
-const ProcessNode = () => (
-
-
- LLM Processing
- GPT-4o
-
-
-
- Processes the input and generates a response.
-
-
-
-);
-
-const OutputNode = () => (
-
-
- Response
- Markdown output
-
-
-
- Displays the generated response to the user.
-
-
-
-);
-
-const initialNodes = [
- { id: "1", type: "input", position: { x: 0, y: 100 }, data: {} },
- { id: "2", type: "process", position: { x: 500, y: 100 }, data: {} },
- { id: "3", type: "output", position: { x: 1000, y: 100 }, data: {} },
-];
-
-const initialEdges = [
- { id: "e1-2", source: "1", target: "2" },
- { id: "e2-3", source: "2", target: "3" },
-];
-
-export function CanvasDemo() {
- const nodeTypes: NodeTypes = useMemo(
- () => ({
- input: InputNode,
- output: OutputNode,
- process: ProcessNode,
- }),
- [],
- );
-
- return (
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/chain-of-thought-demo.tsx b/apps/docs/src/components/docs/ai-elements/chain-of-thought-demo.tsx
deleted file mode 100644
index 5238cc15..00000000
--- a/apps/docs/src/components/docs/ai-elements/chain-of-thought-demo.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-"use client";
-
-import {
- ChainOfThought,
- ChainOfThoughtContent,
- ChainOfThoughtHeader,
- ChainOfThoughtSearchResult,
- ChainOfThoughtSearchResults,
- ChainOfThoughtStep,
-} from "ghost-ui";
-import { DatabaseIcon, FileTextIcon, SearchIcon } from "lucide-react";
-
-export function ChainOfThoughtDemo() {
- return (
-
- Researching climate data
-
-
-
- IPCC 2024
-
- NASA Climate
-
- NOAA Data
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/checkpoint-demo.tsx b/apps/docs/src/components/docs/ai-elements/checkpoint-demo.tsx
deleted file mode 100644
index 5073b3d3..00000000
--- a/apps/docs/src/components/docs/ai-elements/checkpoint-demo.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-"use client";
-
-import { Checkpoint, CheckpointIcon, CheckpointTrigger } from "ghost-ui";
-
-export function CheckpointDemo() {
- return (
-
-
-
-
- Checkpoint 1 — Initial draft
-
-
-
-
- Some conversation content between checkpoints...
-
-
-
-
-
- Checkpoint 2 — After revisions
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/code-block-demo.tsx b/apps/docs/src/components/docs/ai-elements/code-block-demo.tsx
deleted file mode 100644
index 9fa3d01f..00000000
--- a/apps/docs/src/components/docs/ai-elements/code-block-demo.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-"use client";
-
-import {
- CodeBlock,
- CodeBlockActions,
- CodeBlockCopyButton,
- CodeBlockFilename,
- CodeBlockHeader,
- CodeBlockTitle,
-} from "ghost-ui";
-
-const typescriptCode = `interface User {
- id: string;
- name: string;
- email: string;
- role: "admin" | "user" | "guest";
-}
-
-async function getUser(id: string): Promise {
- const response = await fetch(\`/api/users/\${id}\`);
-
- if (!response.ok) {
- throw new Error(\`Failed to fetch user: \${response.statusText}\`);
- }
-
- return response.json();
-}`;
-
-const cssCode = `.container {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- gap: 1.5rem;
- padding: 2rem;
-}
-
-.card {
- border-radius: 0.75rem;
- background: var(--card-bg);
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-}`;
-
-const jsonCode = `{
- "name": "@acme/design-system",
- "version": "2.4.0",
- "dependencies": {
- "react": "^19.0.0",
- "tailwindcss": "^4.0.0"
- }
-}`;
-
-export function CodeBlockDemo() {
- return (
-
-
-
-
- lib/api/users.ts
-
-
-
-
-
-
-
-
-
-
- styles/layout.css
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/commit-demo.tsx b/apps/docs/src/components/docs/ai-elements/commit-demo.tsx
deleted file mode 100644
index 73cb521c..00000000
--- a/apps/docs/src/components/docs/ai-elements/commit-demo.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-"use client";
-
-import {
- Commit,
- CommitActions,
- CommitAuthor,
- CommitAuthorAvatar,
- CommitContent,
- CommitCopyButton,
- CommitFile,
- CommitFileAdditions,
- CommitFileChanges,
- CommitFileDeletions,
- CommitFileIcon,
- CommitFileInfo,
- CommitFilePath,
- CommitFileStatus,
- CommitFiles,
- CommitHash,
- CommitHeader,
- CommitInfo,
- CommitMessage,
- CommitMetadata,
- CommitSeparator,
- CommitTimestamp,
-} from "ghost-ui";
-
-export function CommitDemo() {
- return (
-
-
-
-
-
-
-
-
- feat: add user authentication with OAuth2 support
-
-
- a1b2c3d
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- src/lib/auth/oauth.ts
-
-
-
-
-
-
-
-
-
-
- src/middleware.ts
-
-
-
-
-
-
-
-
-
-
- src/lib/auth/legacy.ts
-
-
-
-
-
-
-
-
-
-
- src/config/auth.config.ts
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- fix: resolve race condition in data fetching
-
-
- f8e9d0c
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/confirmation-demo.tsx b/apps/docs/src/components/docs/ai-elements/confirmation-demo.tsx
deleted file mode 100644
index 772fc14f..00000000
--- a/apps/docs/src/components/docs/ai-elements/confirmation-demo.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-"use client";
-
-import {
- Confirmation,
- ConfirmationAccepted,
- ConfirmationAction,
- ConfirmationActions,
- ConfirmationRejected,
- ConfirmationRequest,
- ConfirmationTitle,
-} from "ghost-ui";
-
-export function ConfirmationDemo() {
- return (
-
-
-
Approval requested
-
-
- The assistant wants to execute rm -rf ./build
-
-
-
- This action will delete the build directory. Do you want to
- proceed?
-
-
-
- Deny
- Approve
-
-
-
-
-
-
Accepted
-
-
- Executed rm -rf ./build
-
-
-
- Action was approved and completed successfully.
-
-
-
-
-
-
-
Rejected
-
-
- Blocked rm -rf ./build
-
-
-
- Action was denied by the user.
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/connection-demo.tsx b/apps/docs/src/components/docs/ai-elements/connection-demo.tsx
deleted file mode 100644
index 629c0c58..00000000
--- a/apps/docs/src/components/docs/ai-elements/connection-demo.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-"use client";
-
-import { type NodeTypes, ReactFlowProvider } from "@xyflow/react";
-import {
- Canvas,
- Connection,
- Node,
- NodeContent,
- NodeHeader,
- NodeTitle,
-} from "ghost-ui";
-import { useMemo } from "react";
-
-const SourceNode = () => (
-
-
- Source
-
-
-
- Drag from the handle to see the custom connection line.
-
-
-
-);
-
-const TargetNode = () => (
-
-
- Target
-
-
- Drop a connection here.
-
-
-);
-
-const initialNodes = [
- { id: "1", type: "source", position: { x: 0, y: 80 }, data: {} },
- { id: "2", type: "target", position: { x: 500, y: 80 }, data: {} },
-];
-
-export function ConnectionDemo() {
- const nodeTypes: NodeTypes = useMemo(
- () => ({
- source: SourceNode,
- target: TargetNode,
- }),
- [],
- );
-
- return (
-
-
- Drag from the source handle to see the animated bezier connection line
- with a circular endpoint indicator.
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/context-demo.tsx b/apps/docs/src/components/docs/ai-elements/context-demo.tsx
deleted file mode 100644
index 6b11729b..00000000
--- a/apps/docs/src/components/docs/ai-elements/context-demo.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-"use client";
-
-import {
- Context,
- ContextContent,
- ContextContentBody,
- ContextContentFooter,
- ContextContentHeader,
- ContextInputUsage,
- ContextOutputUsage,
- ContextTrigger,
-} from "ghost-ui";
-
-export function ContextDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/controls-demo.tsx b/apps/docs/src/components/docs/ai-elements/controls-demo.tsx
deleted file mode 100644
index 2d845c1f..00000000
--- a/apps/docs/src/components/docs/ai-elements/controls-demo.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-"use client";
-
-import { type NodeTypes, ReactFlowProvider } from "@xyflow/react";
-import {
- Canvas,
- Controls,
- Node,
- NodeContent,
- NodeHeader,
- NodeTitle,
-} from "ghost-ui";
-import { useMemo } from "react";
-
-const SampleNode = () => (
-
-
- Sample Node
-
-
-
- Use the controls in the bottom-left to zoom, fit view, and lock
- interactions.
-
-
-
-);
-
-const initialNodes = [
- { id: "1", type: "sample", position: { x: 0, y: 0 }, data: {} },
- { id: "2", type: "sample", position: { x: 400, y: 150 }, data: {} },
-];
-
-export function ControlsDemo() {
- const nodeTypes: NodeTypes = useMemo(
- () => ({
- sample: SampleNode,
- }),
- [],
- );
-
- return (
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/conversation-demo.tsx b/apps/docs/src/components/docs/ai-elements/conversation-demo.tsx
deleted file mode 100644
index 32a994ac..00000000
--- a/apps/docs/src/components/docs/ai-elements/conversation-demo.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-"use client";
-
-import {
- Conversation,
- ConversationContent,
- ConversationEmptyState,
-} from "ghost-ui";
-import { MessageSquareIcon } from "lucide-react";
-
-export function ConversationDemo() {
- return (
-
-
-
- }
- />
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/edge-demo.tsx b/apps/docs/src/components/docs/ai-elements/edge-demo.tsx
deleted file mode 100644
index ec292976..00000000
--- a/apps/docs/src/components/docs/ai-elements/edge-demo.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-"use client";
-
-import {
- type EdgeTypes,
- type NodeTypes,
- ReactFlowProvider,
-} from "@xyflow/react";
-import { Canvas, Edge, Node, NodeHeader, NodeTitle } from "ghost-ui";
-import { useMemo } from "react";
-
-const SimpleNode = ({ data }: { data: { label: string } }) => (
-
-
- {data.label}
-
-
-);
-
-const initialNodes = [
- {
- id: "a1",
- type: "simple",
- position: { x: 0, y: 0 },
- data: { label: "Start" },
- },
- {
- id: "a2",
- type: "simple",
- position: { x: 450, y: 0 },
- data: { label: "Animated" },
- },
- {
- id: "b1",
- type: "simple",
- position: { x: 0, y: 150 },
- data: { label: "Draft" },
- },
- {
- id: "b2",
- type: "simple",
- position: { x: 450, y: 150 },
- data: { label: "Temporary" },
- },
-];
-
-const initialEdges = [
- { id: "e-animated", source: "a1", target: "a2", type: "animated" },
- { id: "e-temporary", source: "b1", target: "b2", type: "temporary" },
-];
-
-export function EdgeDemo() {
- const nodeTypes: NodeTypes = useMemo(
- () => ({
- simple: SimpleNode,
- }),
- [],
- );
-
- const edgeTypes: EdgeTypes = useMemo(
- () => ({
- animated: Edge.Animated,
- temporary: Edge.Temporary,
- }),
- [],
- );
-
- return (
-
-
- Two edge variants: Animated (top, with a traveling dot)
- and Temporary (bottom, dashed line).
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/environment-variables-demo.tsx b/apps/docs/src/components/docs/ai-elements/environment-variables-demo.tsx
deleted file mode 100644
index 8c0d8ce4..00000000
--- a/apps/docs/src/components/docs/ai-elements/environment-variables-demo.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-"use client";
-
-import {
- EnvironmentVariable,
- EnvironmentVariableCopyButton,
- EnvironmentVariableGroup,
- EnvironmentVariableName,
- EnvironmentVariableRequired,
- EnvironmentVariables,
- EnvironmentVariablesContent,
- EnvironmentVariablesHeader,
- EnvironmentVariablesTitle,
- EnvironmentVariablesToggle,
- EnvironmentVariableValue,
-} from "ghost-ui";
-
-export function EnvironmentVariablesDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/file-tree-demo.tsx b/apps/docs/src/components/docs/ai-elements/file-tree-demo.tsx
deleted file mode 100644
index c2ce9e49..00000000
--- a/apps/docs/src/components/docs/ai-elements/file-tree-demo.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-
-import { FileTree, FileTreeFile, FileTreeFolder } from "ghost-ui";
-
-export function FileTreeDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/image-demo.tsx b/apps/docs/src/components/docs/ai-elements/image-demo.tsx
deleted file mode 100644
index 6511c174..00000000
--- a/apps/docs/src/components/docs/ai-elements/image-demo.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-"use client";
-
-import { Image } from "ghost-ui";
-
-// A tiny 1x1 transparent PNG placeholder
-const PLACEHOLDER_BASE64 =
- "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
-
-export function ImageDemo() {
- return (
-
-
- Renders an AI-generated image from base64 data. The component
- automatically constructs a data URI from the provided media type and
- base64 string.
-
-
-
-
-
-
- PNG, landscape aspect ratio
-
-
-
-
-
-
- PNG, square aspect ratio
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/index.tsx b/apps/docs/src/components/docs/ai-elements/index.tsx
deleted file mode 100644
index 46e71e02..00000000
--- a/apps/docs/src/components/docs/ai-elements/index.tsx
+++ /dev/null
@@ -1,238 +0,0 @@
-"use client";
-
-// Code
-import { AgentDemo } from "@/components/docs/ai-elements/agent-demo";
-import { ArtifactDemo } from "@/components/docs/ai-elements/artifact-demo";
-// Chatbot
-import { AttachmentsDemo } from "@/components/docs/ai-elements/attachments-demo";
-// Voice
-import { AudioPlayerDemo } from "@/components/docs/ai-elements/audio-player-demo";
-// Workflow
-import { CanvasDemo } from "@/components/docs/ai-elements/canvas-demo";
-import { ChainOfThoughtDemo } from "@/components/docs/ai-elements/chain-of-thought-demo";
-import { CheckpointDemo } from "@/components/docs/ai-elements/checkpoint-demo";
-import { CodeBlockDemo } from "@/components/docs/ai-elements/code-block-demo";
-import { CommitDemo } from "@/components/docs/ai-elements/commit-demo";
-import { ConfirmationDemo } from "@/components/docs/ai-elements/confirmation-demo";
-import { ConnectionDemo } from "@/components/docs/ai-elements/connection-demo";
-import { ContextDemo } from "@/components/docs/ai-elements/context-demo";
-import { ControlsDemo } from "@/components/docs/ai-elements/controls-demo";
-import { ConversationDemo } from "@/components/docs/ai-elements/conversation-demo";
-import { EdgeDemo } from "@/components/docs/ai-elements/edge-demo";
-import { EnvironmentVariablesDemo } from "@/components/docs/ai-elements/environment-variables-demo";
-import { FileTreeDemo } from "@/components/docs/ai-elements/file-tree-demo";
-// Utilities
-import { ImageDemo } from "@/components/docs/ai-elements/image-demo";
-import { InlineCitationDemo } from "@/components/docs/ai-elements/inline-citation-demo";
-import { JsxPreviewDemo } from "@/components/docs/ai-elements/jsx-preview-demo";
-import { MessageDemo } from "@/components/docs/ai-elements/message-demo";
-import { MicSelectorDemo } from "@/components/docs/ai-elements/mic-selector-demo";
-import { ModelSelectorDemo } from "@/components/docs/ai-elements/model-selector-demo";
-import { NodeDemo } from "@/components/docs/ai-elements/node-demo";
-import { OpenInChatDemo } from "@/components/docs/ai-elements/open-in-chat-demo";
-import { PackageInfoDemo } from "@/components/docs/ai-elements/package-info-demo";
-import { PanelDemo } from "@/components/docs/ai-elements/panel-demo";
-import { PersonaDemo } from "@/components/docs/ai-elements/persona-demo";
-import { PlanDemo } from "@/components/docs/ai-elements/plan-demo";
-import { PromptInputDemo } from "@/components/docs/ai-elements/prompt-input-demo";
-import { QueueDemo } from "@/components/docs/ai-elements/queue-demo";
-import { ReasoningDemo } from "@/components/docs/ai-elements/reasoning-demo";
-import { SandboxDemo } from "@/components/docs/ai-elements/sandbox-demo";
-import { SchemaDisplayDemo } from "@/components/docs/ai-elements/schema-display-demo";
-import { ShimmerDemo } from "@/components/docs/ai-elements/shimmer-demo";
-import { SnippetDemo } from "@/components/docs/ai-elements/snippet-demo";
-import { SourcesDemo } from "@/components/docs/ai-elements/sources-demo";
-import { SpeechInputDemo } from "@/components/docs/ai-elements/speech-input-demo";
-import { StackTraceDemo } from "@/components/docs/ai-elements/stack-trace-demo";
-import { SuggestionDemo } from "@/components/docs/ai-elements/suggestion-demo";
-import { TaskDemo } from "@/components/docs/ai-elements/task-demo";
-import { TerminalDemo } from "@/components/docs/ai-elements/terminal-demo";
-import { TestResultsDemo } from "@/components/docs/ai-elements/test-results-demo";
-import { ToolDemo } from "@/components/docs/ai-elements/tool-demo";
-import { ToolbarDemo } from "@/components/docs/ai-elements/toolbar-demo";
-import { TranscriptionDemo } from "@/components/docs/ai-elements/transcription-demo";
-import { VoiceSelectorDemo } from "@/components/docs/ai-elements/voice-selector-demo";
-import { WebPreviewDemo } from "@/components/docs/ai-elements/web-preview-demo";
-import { ComponentWrapper } from "@/components/docs/primitives/component-wrapper";
-
-function CategoryLabel({ children }: { children: React.ReactNode }) {
- return (
-
- );
-}
-
-export function AIElementDemos() {
- return (
-
- {/* Chatbot */}
-
Chatbot
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Code */}
-
Code
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Voice */}
-
Voice
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Workflow */}
-
Workflow
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Utilities */}
-
Utilities
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/inline-citation-demo.tsx b/apps/docs/src/components/docs/ai-elements/inline-citation-demo.tsx
deleted file mode 100644
index 32219032..00000000
--- a/apps/docs/src/components/docs/ai-elements/inline-citation-demo.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-"use client";
-
-import {
- InlineCitation,
- InlineCitationCard,
- InlineCitationCardBody,
- InlineCitationCardTrigger,
- InlineCitationCarousel,
- InlineCitationCarouselContent,
- InlineCitationCarouselHeader,
- InlineCitationCarouselIndex,
- InlineCitationCarouselItem,
- InlineCitationCarouselNext,
- InlineCitationCarouselPrev,
- InlineCitationSource,
- InlineCitationText,
-} from "ghost-ui";
-
-const sources = [
- "https://en.wikipedia.org/wiki/Large_language_model",
- "https://arxiv.org/abs/2303.08774",
-];
-
-export function InlineCitationDemo() {
- return (
-
- Large language models are neural networks trained on vast amounts of text
- data.{" "}
-
-
- They use transformer architectures to generate coherent text
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {" "}
- and have become a cornerstone of modern AI applications.
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/jsx-preview-demo.tsx b/apps/docs/src/components/docs/ai-elements/jsx-preview-demo.tsx
deleted file mode 100644
index ffa7d6f9..00000000
--- a/apps/docs/src/components/docs/ai-elements/jsx-preview-demo.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-"use client";
-
-import { JSXPreview, JSXPreviewContent, JSXPreviewError } from "ghost-ui";
-
-const validJsx = `
-
Welcome Back
-
Your dashboard is ready to explore.
-
- View Reports
- Get Started
-
-
`;
-
-const errorJsx = `
-
-
`;
-
-export function JsxPreviewDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/message-demo.tsx b/apps/docs/src/components/docs/ai-elements/message-demo.tsx
deleted file mode 100644
index 40d6673e..00000000
--- a/apps/docs/src/components/docs/ai-elements/message-demo.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-"use client";
-
-import {
- Message,
- MessageAction,
- MessageActions,
- MessageContent,
- MessageResponse,
-} from "ghost-ui";
-import {
- CopyIcon,
- RefreshCwIcon,
- ThumbsDownIcon,
- ThumbsUpIcon,
-} from "lucide-react";
-
-export function MessageDemo() {
- return (
-
-
-
- Can you explain how React Server Components work?
-
-
-
-
-
-
- {`**React Server Components** (RSC) allow you to render components on the server, reducing the amount of JavaScript sent to the client.\n\n### Key Benefits\n\n- **Zero bundle size** — Server Components are not included in the client bundle\n- **Direct backend access** — You can query databases directly\n- **Automatic code splitting** — Client components are lazy-loaded\n\n\`\`\`tsx\n// This runs on the server\nasync function UserProfile({ id }: { id: string }) {\n const user = await db.user.findUnique({ where: { id } });\n return {user.name}
;\n}\n\`\`\``}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/mic-selector-demo.tsx b/apps/docs/src/components/docs/ai-elements/mic-selector-demo.tsx
deleted file mode 100644
index 06786dd3..00000000
--- a/apps/docs/src/components/docs/ai-elements/mic-selector-demo.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-"use client";
-
-import {
- MicSelector,
- MicSelectorContent,
- MicSelectorEmpty,
- MicSelectorInput,
- MicSelectorItem,
- MicSelectorLabel,
- MicSelectorList,
- MicSelectorTrigger,
- MicSelectorValue,
-} from "ghost-ui";
-
-export function MicSelectorDemo() {
- return (
-
-
- Opens a popover listing available audio input devices. Requires
- microphone permission to show device names.
-
-
-
-
-
-
-
-
- {(devices) =>
- devices.length === 0 ? (
-
- ) : (
- devices.map((device) => (
-
-
-
- ))
- )
- }
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/model-selector-demo.tsx b/apps/docs/src/components/docs/ai-elements/model-selector-demo.tsx
deleted file mode 100644
index df2e74b1..00000000
--- a/apps/docs/src/components/docs/ai-elements/model-selector-demo.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-"use client";
-
-import {
- Button,
- ModelSelector,
- ModelSelectorContent,
- ModelSelectorEmpty,
- ModelSelectorGroup,
- ModelSelectorInput,
- ModelSelectorItem,
- ModelSelectorList,
- ModelSelectorLogo,
- ModelSelectorLogoGroup,
- ModelSelectorName,
- ModelSelectorTrigger,
-} from "ghost-ui";
-
-export function ModelSelectorDemo() {
- return (
-
-
- Select a model
-
-
-
-
- No models found.
-
-
-
-
-
- GPT-4o
-
-
-
-
-
- GPT-4o Mini
-
-
-
-
-
-
-
- Claude Sonnet 4
-
-
-
-
-
- Claude Opus 4
-
-
-
-
-
-
-
- Gemini 2.5 Pro
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/node-demo.tsx b/apps/docs/src/components/docs/ai-elements/node-demo.tsx
deleted file mode 100644
index 922f9323..00000000
--- a/apps/docs/src/components/docs/ai-elements/node-demo.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-"use client";
-
-import { ReactFlowProvider } from "@xyflow/react";
-import {
- Badge,
- Button,
- Node,
- NodeContent,
- NodeDescription,
- NodeFooter,
- NodeHeader,
- NodeTitle,
-} from "ghost-ui";
-
-export function NodeDemo() {
- return (
-
-
-
-
-
- Full Node
-
- A node with header, content, and footer
-
-
-
-
- This node demonstrates all available sub-components arranged
- together. It has both target (left) and source (right) handles.
-
-
-
- Ready
-
- Run
-
-
-
-
-
-
- Source Only
- Starting node in a workflow
-
-
-
- This node only has a source handle on the right side.
-
-
-
-
-
-
- Target Only
- Terminal node in a workflow
-
-
-
- This node only has a target handle on the left side.
-
-
-
-
-
-
- Standalone
- No handles
-
-
-
- A standalone card-style node with no connection handles.
-
-
-
- Idle
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/open-in-chat-demo.tsx b/apps/docs/src/components/docs/ai-elements/open-in-chat-demo.tsx
deleted file mode 100644
index f5e1c694..00000000
--- a/apps/docs/src/components/docs/ai-elements/open-in-chat-demo.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-"use client";
-
-import {
- OpenIn,
- OpenInChatGPT,
- OpenInClaude,
- OpenInContent,
- OpenInCursor,
- OpenInLabel,
- OpenInScira,
- OpenInSeparator,
- OpenInT3,
- OpenInTrigger,
- OpenInv0,
-} from "ghost-ui";
-
-export function OpenInChatDemo() {
- return (
-
-
- A dropdown menu that lets users open a query in various AI chat
- providers. Each item generates a provider-specific URL and opens it in a
- new tab.
-
-
-
-
-
- Open in a chat provider
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/package-info-demo.tsx b/apps/docs/src/components/docs/ai-elements/package-info-demo.tsx
deleted file mode 100644
index d6d393dd..00000000
--- a/apps/docs/src/components/docs/ai-elements/package-info-demo.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-"use client";
-
-import {
- PackageInfo,
- PackageInfoContent,
- PackageInfoDependencies,
- PackageInfoDependency,
- PackageInfoDescription,
-} from "ghost-ui";
-
-export function PackageInfoDemo() {
- return (
-
-
-
- A JavaScript library for building user interfaces
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/panel-demo.tsx b/apps/docs/src/components/docs/ai-elements/panel-demo.tsx
deleted file mode 100644
index ec110916..00000000
--- a/apps/docs/src/components/docs/ai-elements/panel-demo.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-"use client";
-
-import { type NodeTypes, ReactFlowProvider } from "@xyflow/react";
-import {
- Canvas,
- Node,
- NodeContent,
- NodeHeader,
- NodeTitle,
- Panel,
-} from "ghost-ui";
-import { useMemo } from "react";
-
-const PlaceholderNode = () => (
-
-
- Workflow Node
-
-
-
- Panels float above the canvas at fixed positions.
-
-
-
-);
-
-const initialNodes = [
- { id: "1", type: "placeholder", position: { x: 100, y: 80 }, data: {} },
-];
-
-export function PanelDemo() {
- const nodeTypes: NodeTypes = useMemo(
- () => ({
- placeholder: PlaceholderNode,
- }),
- [],
- );
-
- return (
-
-
- Panels are floating overlays positioned at the edges of the canvas.
-
-
-
-
-
- Top Left Panel
-
-
- Top Right Panel
-
-
- Bottom Center Panel
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/persona-demo.tsx b/apps/docs/src/components/docs/ai-elements/persona-demo.tsx
deleted file mode 100644
index f5e48571..00000000
--- a/apps/docs/src/components/docs/ai-elements/persona-demo.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-"use client";
-
-import type { PersonaState } from "ghost-ui";
-import { Button, Persona } from "ghost-ui";
-import { useState } from "react";
-
-const variants = [
- "obsidian",
- "glint",
- "halo",
- "command",
- "mana",
- "opal",
-] as const;
-const states: PersonaState[] = [
- "idle",
- "listening",
- "thinking",
- "speaking",
- "asleep",
-];
-
-export function PersonaDemo() {
- const [currentState, setCurrentState] = useState("idle");
-
- return (
-
-
- {states.map((s) => (
- setCurrentState(s)}
- >
- {s}
-
- ))}
-
-
-
- {variants.map((variant) => (
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/plan-demo.tsx b/apps/docs/src/components/docs/ai-elements/plan-demo.tsx
deleted file mode 100644
index ce00bc8d..00000000
--- a/apps/docs/src/components/docs/ai-elements/plan-demo.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-"use client";
-
-import {
- Plan,
- PlanAction,
- PlanContent,
- PlanDescription,
- PlanFooter,
- PlanHeader,
- PlanTitle,
- PlanTrigger,
-} from "ghost-ui";
-import { CheckCircleIcon, CircleIcon } from "lucide-react";
-
-export function PlanDemo() {
- return (
-
-
-
-
-
Build a Landing Page
-
- Create a responsive landing page with hero section, features, and
- footer.
-
-
-
-
-
-
-
-
-
-
-
- Set up project structure
-
-
-
-
-
- Design hero section
-
-
-
-
- Build features grid
-
-
-
- Add footer and navigation
-
-
-
-
-
- 2 of 4 steps completed
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/prompt-input-demo.tsx b/apps/docs/src/components/docs/ai-elements/prompt-input-demo.tsx
deleted file mode 100644
index 47132fcf..00000000
--- a/apps/docs/src/components/docs/ai-elements/prompt-input-demo.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-"use client";
-
-import {
- PromptInput,
- PromptInputButton,
- PromptInputFooter,
- PromptInputSubmit,
- PromptInputTextarea,
- PromptInputTools,
-} from "ghost-ui";
-import { PaperclipIcon } from "lucide-react";
-
-export function PromptInputDemo() {
- return (
-
-
{}}>
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/queue-demo.tsx b/apps/docs/src/components/docs/ai-elements/queue-demo.tsx
deleted file mode 100644
index b6f9b370..00000000
--- a/apps/docs/src/components/docs/ai-elements/queue-demo.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-"use client";
-
-import {
- Queue,
- QueueItem,
- QueueItemContent,
- QueueItemDescription,
- QueueItemIndicator,
- QueueList,
- QueueSection,
- QueueSectionContent,
- QueueSectionLabel,
- QueueSectionTrigger,
-} from "ghost-ui";
-import { CheckCircleIcon, ListTodoIcon } from "lucide-react";
-
-export function QueueDemo() {
- return (
-
-
-
-
- }
- />
-
-
-
-
-
-
-
- Refactor authentication module
-
-
-
- Extract shared logic into a reusable hook
-
-
-
-
-
-
- Write unit tests for API routes
-
-
-
-
-
-
-
-
-
- }
- />
-
-
-
-
-
-
-
- Set up project scaffolding
-
-
-
-
-
-
-
- Configure database schema
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/reasoning-demo.tsx b/apps/docs/src/components/docs/ai-elements/reasoning-demo.tsx
deleted file mode 100644
index 0ca165bd..00000000
--- a/apps/docs/src/components/docs/ai-elements/reasoning-demo.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-"use client";
-
-import { Reasoning, ReasoningContent, ReasoningTrigger } from "ghost-ui";
-
-export function ReasoningDemo() {
- return (
-
-
-
Completed reasoning
-
-
-
- {`The user is asking about the performance implications of using React Server Components. Let me think through the key factors:\n\n1. **Bundle size reduction** - Since server components don't ship JavaScript to the client, the initial bundle can be significantly smaller.\n\n2. **Data fetching** - Server components can fetch data directly during rendering, eliminating client-side waterfalls.\n\n3. **Streaming** - The server can stream HTML progressively, improving Time to First Byte.`}
-
-
-
-
-
-
Streaming reasoning
-
-
-
- {`Analyzing the query about database optimization strategies. I should consider indexing, query planning, and caching...`}
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/sandbox-demo.tsx b/apps/docs/src/components/docs/ai-elements/sandbox-demo.tsx
deleted file mode 100644
index 55604449..00000000
--- a/apps/docs/src/components/docs/ai-elements/sandbox-demo.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-"use client";
-
-import {
- CodeBlock,
- CodeBlockActions,
- CodeBlockCopyButton,
- CodeBlockFilename,
- CodeBlockHeader,
- CodeBlockTitle,
- Sandbox,
- SandboxContent,
- SandboxHeader,
- SandboxTabContent,
- SandboxTabs,
- SandboxTabsBar,
- SandboxTabsList,
- SandboxTabsTrigger,
-} from "ghost-ui";
-
-const codeSnippet = `import { useState } from "react";
-
-export default function Counter() {
- const [count, setCount] = useState(0);
-
- return (
-
-
{count}
- setCount(c => c + 1)}>
- Increment
-
-
- );
-}`;
-
-const outputText = `> next dev --turbo
- Ready in 1.2s
- Local: http://localhost:3000
- Network: http://192.168.1.100:3000
-
- Compiled /page in 340ms`;
-
-export function SandboxDemo() {
- return (
-
-
-
-
-
-
-
- Code
- Output
-
-
-
-
-
-
- counter.tsx
-
-
-
-
-
-
-
-
- {outputText}
-
-
-
-
-
-
-
-
-
- Executing test suite...
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/schema-display-demo.tsx b/apps/docs/src/components/docs/ai-elements/schema-display-demo.tsx
deleted file mode 100644
index 12c4c54b..00000000
--- a/apps/docs/src/components/docs/ai-elements/schema-display-demo.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-"use client";
-
-import { SchemaDisplay } from "ghost-ui";
-
-export function SchemaDisplayDemo() {
- return (
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/shimmer-demo.tsx b/apps/docs/src/components/docs/ai-elements/shimmer-demo.tsx
deleted file mode 100644
index 2eb428f5..00000000
--- a/apps/docs/src/components/docs/ai-elements/shimmer-demo.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-"use client";
-
-import { Shimmer } from "ghost-ui";
-
-export function ShimmerDemo() {
- return (
-
-
- Generating response...
-
-
-
- Analyzing your code and preparing suggestions
-
-
-
- Thinking...
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/snippet-demo.tsx b/apps/docs/src/components/docs/ai-elements/snippet-demo.tsx
deleted file mode 100644
index 56d45b7b..00000000
--- a/apps/docs/src/components/docs/ai-elements/snippet-demo.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-"use client";
-
-import {
- Snippet,
- SnippetAddon,
- SnippetCopyButton,
- SnippetInput,
- SnippetText,
-} from "ghost-ui";
-
-export function SnippetDemo() {
- return (
-
-
-
- $
-
-
-
-
-
-
-
-
-
- $
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/sources-demo.tsx b/apps/docs/src/components/docs/ai-elements/sources-demo.tsx
deleted file mode 100644
index 598afb1c..00000000
--- a/apps/docs/src/components/docs/ai-elements/sources-demo.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-"use client";
-
-import { Source, Sources, SourcesContent, SourcesTrigger } from "ghost-ui";
-
-export function SourcesDemo() {
- return (
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/speech-input-demo.tsx b/apps/docs/src/components/docs/ai-elements/speech-input-demo.tsx
deleted file mode 100644
index 9d75873d..00000000
--- a/apps/docs/src/components/docs/ai-elements/speech-input-demo.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-"use client";
-
-import { SpeechInput } from "ghost-ui";
-import { useState } from "react";
-
-export function SpeechInputDemo() {
- const [transcript, setTranscript] = useState("");
-
- return (
-
-
- Click the microphone button to begin recording. Uses the Web Speech API
- when available, with a MediaRecorder fallback.
-
-
-
- setTranscript((prev) => (prev ? `${prev} ${text}` : text))
- }
- />
-
-
-
Transcription output
-
- {transcript || "Transcribed text will appear here..."}
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/stack-trace-demo.tsx b/apps/docs/src/components/docs/ai-elements/stack-trace-demo.tsx
deleted file mode 100644
index b8863d15..00000000
--- a/apps/docs/src/components/docs/ai-elements/stack-trace-demo.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-"use client";
-
-import {
- StackTrace,
- StackTraceActions,
- StackTraceContent,
- StackTraceCopyButton,
- StackTraceError,
- StackTraceErrorMessage,
- StackTraceErrorType,
- StackTraceExpandButton,
- StackTraceFrames,
- StackTraceHeader,
-} from "ghost-ui";
-
-const typeErrorTrace = `TypeError: Cannot read properties of undefined (reading 'map')
- at UserList (/src/components/UserList.tsx:24:18)
- at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14985:18)
- at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:17811:13)
- at beginWork (node_modules/react-dom/cjs/react-dom.development.js:19049:16)
- at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:3945:14)`;
-
-const referenceErrorTrace = `ReferenceError: fetchData is not defined
- at loadDashboard (/src/pages/dashboard.ts:15:3)
- at async handleRequest (/src/server/router.ts:42:12)
- at async processMiddleware (/src/server/middleware.ts:28:5)
- at node:internal/process/task_queues:95:5`;
-
-export function StackTraceDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/suggestion-demo.tsx b/apps/docs/src/components/docs/ai-elements/suggestion-demo.tsx
deleted file mode 100644
index feea6511..00000000
--- a/apps/docs/src/components/docs/ai-elements/suggestion-demo.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-"use client";
-
-import { Suggestion, Suggestions } from "ghost-ui";
-
-export function SuggestionDemo() {
- return (
-
-
Suggested prompts
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/task-demo.tsx b/apps/docs/src/components/docs/ai-elements/task-demo.tsx
deleted file mode 100644
index fd752437..00000000
--- a/apps/docs/src/components/docs/ai-elements/task-demo.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-"use client";
-
-import {
- Task,
- TaskContent,
- TaskItem,
- TaskItemFile,
- TaskTrigger,
-} from "ghost-ui";
-
-export function TaskDemo() {
- return (
-
-
-
-
-
- Found src/auth/login.ts — contains the
- main login handler with JWT token generation.
-
-
- Found src/middleware/auth.ts —
- validates tokens on protected routes.
-
-
- Found src/lib/session.ts — manages
- session creation and expiration.
-
-
-
-
-
-
-
-
- Reviewed prisma/schema.prisma for user
- model relationships.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/terminal-demo.tsx b/apps/docs/src/components/docs/ai-elements/terminal-demo.tsx
deleted file mode 100644
index 6142dca7..00000000
--- a/apps/docs/src/components/docs/ai-elements/terminal-demo.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-"use client";
-
-import {
- Terminal,
- TerminalActions,
- TerminalContent,
- TerminalCopyButton,
- TerminalHeader,
- TerminalTitle,
-} from "ghost-ui";
-
-const buildOutput = `\x1b[32m$\x1b[0m next build
-\x1b[36minfo\x1b[0m - Linting and checking validity of types...
-\x1b[36minfo\x1b[0m - Creating an optimized production build...
-\x1b[36minfo\x1b[0m - Compiled successfully
-\x1b[36minfo\x1b[0m - Collecting page data...
-\x1b[36minfo\x1b[0m - Generating static pages (8/8)
-\x1b[36minfo\x1b[0m - Finalizing page optimization...
-
-Route (app) Size First Load JS
-\x1b[37m+\x1b[0m / 5.2 kB 89.3 kB
-\x1b[37m+\x1b[0m /about 1.8 kB 85.9 kB
-\x1b[37m+\x1b[0m /dashboard 12.4 kB 96.5 kB
-\x1b[37m+\x1b[0m /api/health 0 B 0 B
-
-\x1b[32m\u2713\x1b[0m Build completed in 14.2s`;
-
-const gitOutput = `\x1b[33mOn branch main\x1b[0m
-Your branch is up to date with 'origin/main'.
-
-Changes to be committed:
- \x1b[32mnew file: src/components/avatar.tsx\x1b[0m
- \x1b[32mmodified: src/lib/utils.ts\x1b[0m
- \x1b[31mdeleted: src/old-component.tsx\x1b[0m
-
-Untracked files:
- \x1b[31m.env.local\x1b[0m`;
-
-export function TerminalDemo() {
- return (
-
-
-
- Build Output
-
-
-
-
-
-
-
-
-
- git status
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/test-results-demo.tsx b/apps/docs/src/components/docs/ai-elements/test-results-demo.tsx
deleted file mode 100644
index e3811432..00000000
--- a/apps/docs/src/components/docs/ai-elements/test-results-demo.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-"use client";
-
-import {
- Test,
- TestError,
- TestErrorMessage,
- TestErrorStack,
- TestResults,
- TestResultsContent,
- TestResultsDuration,
- TestResultsHeader,
- TestResultsProgress,
- TestResultsSummary,
- TestSuite,
- TestSuiteContent,
- TestSuiteName,
-} from "ghost-ui";
-
-export function TestResultsDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Expected status 200 but received 500
-
-
- {`at Object. (tests/api/user.test.ts:45:10)
- at processTicksAndRejections (node:internal/process/task_queues:95:5)`}
-
-
-
-
-
-
- AssertionError: expected 'invalid' to match
- /^[^@]+@[^@]+$/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/tool-demo.tsx b/apps/docs/src/components/docs/ai-elements/tool-demo.tsx
deleted file mode 100644
index db1d9e3d..00000000
--- a/apps/docs/src/components/docs/ai-elements/tool-demo.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-"use client";
-
-import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from "ghost-ui";
-
-export function ToolDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/toolbar-demo.tsx b/apps/docs/src/components/docs/ai-elements/toolbar-demo.tsx
deleted file mode 100644
index 0d6a2cf5..00000000
--- a/apps/docs/src/components/docs/ai-elements/toolbar-demo.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-"use client";
-
-import { type NodeTypes, ReactFlowProvider } from "@xyflow/react";
-import {
- Button,
- Canvas,
- Node,
- NodeContent,
- NodeHeader,
- NodeTitle,
- Toolbar,
-} from "ghost-ui";
-import { CopyIcon, PencilIcon, Trash2Icon } from "lucide-react";
-import { useMemo } from "react";
-
-const ToolbarNode = () => (
-
-
- Select this node
-
-
-
- Click to select and reveal the toolbar below.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-);
-
-const initialNodes = [
- {
- id: "1",
- type: "toolbar",
- position: { x: 100, y: 60 },
- data: {},
- selected: true,
- },
-];
-
-export function ToolbarDemo() {
- const nodeTypes: NodeTypes = useMemo(
- () => ({
- toolbar: ToolbarNode,
- }),
- [],
- );
-
- return (
-
-
- A floating toolbar that appears below a selected node, providing
- contextual actions.
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/transcription-demo.tsx b/apps/docs/src/components/docs/ai-elements/transcription-demo.tsx
deleted file mode 100644
index 43c17faf..00000000
--- a/apps/docs/src/components/docs/ai-elements/transcription-demo.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-"use client";
-
-import { Button, Transcription, TranscriptionSegment } from "ghost-ui";
-import { useState } from "react";
-
-const mockSegments = [
- { text: "Welcome to the demo.", startSecond: 0, endSecond: 2 },
- { text: " Today we are looking at", startSecond: 2, endSecond: 4 },
- { text: " the transcription component,", startSecond: 4, endSecond: 6 },
- { text: " which highlights words", startSecond: 6, endSecond: 8 },
- { text: " as audio plays.", startSecond: 8, endSecond: 10 },
- { text: " Each segment is clickable", startSecond: 10, endSecond: 12 },
- { text: " and can seek to", startSecond: 12, endSecond: 14 },
- { text: " the corresponding position", startSecond: 14, endSecond: 16 },
- { text: " in the audio track.", startSecond: 16, endSecond: 18 },
-];
-
-export function TranscriptionDemo() {
- const [currentTime, setCurrentTime] = useState(0);
-
- return (
-
-
- setCurrentTime((t) => Math.max(0, t - 2))}
- >
- -2s
-
-
- {currentTime.toFixed(1)}s
-
- setCurrentTime((t) => Math.min(18, t + 2))}
- >
- +2s
-
-
-
-
- {(segment, index) => (
-
- )}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/voice-selector-demo.tsx b/apps/docs/src/components/docs/ai-elements/voice-selector-demo.tsx
deleted file mode 100644
index a5fe8b04..00000000
--- a/apps/docs/src/components/docs/ai-elements/voice-selector-demo.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-"use client";
-
-import {
- Button,
- VoiceSelector,
- VoiceSelectorAccent,
- VoiceSelectorAge,
- VoiceSelectorAttributes,
- VoiceSelectorBullet,
- VoiceSelectorContent,
- VoiceSelectorDescription,
- VoiceSelectorEmpty,
- VoiceSelectorGender,
- VoiceSelectorGroup,
- VoiceSelectorInput,
- VoiceSelectorItem,
- VoiceSelectorList,
- VoiceSelectorName,
- VoiceSelectorPreview,
- VoiceSelectorSeparator,
- VoiceSelectorTrigger,
-} from "ghost-ui";
-import { useState } from "react";
-
-const voices = [
- {
- id: "alloy",
- name: "Alloy",
- gender: "non-binary" as const,
- accent: "american" as const,
- age: "Young Adult",
- description: "Versatile, balanced tone",
- },
- {
- id: "echo",
- name: "Echo",
- gender: "male" as const,
- accent: "american" as const,
- age: "Adult",
- description: "Warm, resonant baritone",
- },
- {
- id: "fable",
- name: "Fable",
- gender: "female" as const,
- accent: "british" as const,
- age: "Adult",
- description: "Expressive storyteller",
- },
- {
- id: "onyx",
- name: "Onyx",
- gender: "male" as const,
- accent: "american" as const,
- age: "Mature",
- description: "Deep, authoritative",
- },
- {
- id: "nova",
- name: "Nova",
- gender: "female" as const,
- accent: "australian" as const,
- age: "Young Adult",
- description: "Bright, energetic",
- },
- {
- id: "shimmer",
- name: "Shimmer",
- gender: "female" as const,
- accent: "irish" as const,
- age: "Adult",
- description: "Soft, calming presence",
- },
-];
-
-export function VoiceSelectorDemo() {
- const [selected, setSelected] = useState(undefined);
-
- return (
-
-
- A dialog-based voice picker with search, gender, accent, and preview
- controls.
-
-
-
-
-
- {selected
- ? (voices.find((v) => v.id === selected)?.name ?? "Select voice")
- : "Select a voice..."}
-
-
-
-
-
-
- No voices found.
-
- {voices.map((voice) => (
- setSelected(voice.id)}
- >
-
-
-
- {voice.name}
-
-
-
-
-
- {voice.age}
-
-
-
- {voice.description}
-
-
-
{
- /* no-op in demo */
- }}
- />
-
-
- ))}
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/ai-elements/web-preview-demo.tsx b/apps/docs/src/components/docs/ai-elements/web-preview-demo.tsx
deleted file mode 100644
index 76c8253b..00000000
--- a/apps/docs/src/components/docs/ai-elements/web-preview-demo.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-"use client";
-
-import {
- WebPreview,
- WebPreviewBody,
- WebPreviewConsole,
- WebPreviewNavigation,
- WebPreviewNavigationButton,
- WebPreviewUrl,
-} from "ghost-ui";
-import { ArrowLeftIcon, ArrowRightIcon, RefreshCwIcon } from "lucide-react";
-
-export function WebPreviewDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/activity-goal.tsx b/apps/docs/src/components/docs/bento/activity-goal.tsx
deleted file mode 100644
index f7f1c2d4..00000000
--- a/apps/docs/src/components/docs/bento/activity-goal.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-"use client";
-
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
-} from "ghost-ui";
-import { Minus, Plus } from "lucide-react";
-import * as React from "react";
-
-export function CardsActivityGoal() {
- const [amount, setAmount] = React.useState(350);
-
- function onClick(adjustment: number) {
- setAmount(Math.max(200, Math.min(400, amount + adjustment)));
- }
-
- return (
-
-
- Payment Amount
- Set your payment amount.
-
-
-
-
onClick(-10)}
- disabled={amount <= 200}
- >
-
- Decrease
-
-
-
onClick(10)}
- disabled={amount >= 400}
- >
-
- Increase
-
-
-
-
- Pay Now
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/calendar.tsx b/apps/docs/src/components/docs/bento/calendar.tsx
deleted file mode 100644
index e1971caf..00000000
--- a/apps/docs/src/components/docs/bento/calendar.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-"use client";
-
-import { addDays } from "date-fns";
-import { Calendar, Card, CardContent } from "ghost-ui";
-
-const start = new Date(2023, 5, 5);
-
-export function CardsCalendar() {
- return (
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/chat.tsx b/apps/docs/src/components/docs/bento/chat.tsx
deleted file mode 100644
index 85663470..00000000
--- a/apps/docs/src/components/docs/bento/chat.tsx
+++ /dev/null
@@ -1,249 +0,0 @@
-"use client";
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Button,
- Card,
- CardContent,
- CardFooter,
- CardHeader,
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
- cn,
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- Input,
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "ghost-ui";
-import { Check, Plus, Send } from "lucide-react";
-import * as React from "react";
-
-const users = [
- {
- name: "Olivia Martin",
- email: "m@example.com",
- },
- {
- name: "Isabella Nguyen",
- email: "isabella.nguyen@email.com",
- },
- {
- name: "Emma Wilson",
- email: "emma@example.com",
- },
- {
- name: "Jackson Lee",
- email: "lee@example.com",
- },
- {
- name: "William Kim",
- email: "will@email.com",
- },
-] as const;
-
-type User = (typeof users)[number];
-
-export function CardsChat() {
- const [open, setOpen] = React.useState(false);
- const [selectedUsers, setSelectedUsers] = React.useState([]);
-
- const [messages, setMessages] = React.useState([
- {
- role: "agent",
- content: "Hi, how can I help you today?",
- },
- {
- role: "user",
- content: "Hey, I'm having trouble with my account.",
- },
- {
- role: "agent",
- content: "What seems to be the problem?",
- },
- {
- role: "user",
- content: "I can't log in.",
- },
- ]);
- const [input, setInput] = React.useState("");
- const inputLength = input.trim().length;
-
- return (
- <>
-
-
-
-
- OM
-
-
-
Sofia Davis
-
m@example.com
-
-
-
-
-
- setOpen(true)}
- >
-
- New message
-
-
- New message
-
-
-
-
-
- {messages.map((message, index) => (
-
- {message.content}
-
- ))}
-
-
-
-
-
-
-
-
-
- New message
-
- Invite a user to this thread. This will create a new group
- message.
-
-
-
-
-
- No users found.
-
- {users.map((user) => (
- {
- if (selectedUsers.includes(user)) {
- return setSelectedUsers(
- selectedUsers.filter(
- (selectedUser) => selectedUser !== user,
- ),
- );
- }
-
- return setSelectedUsers(
- [...users].filter((u) =>
- [...selectedUsers, user].includes(u),
- ),
- );
- }}
- >
-
- {user.name[0]}
-
-
-
- {user.name}
-
-
- {user.email}
-
-
- {selectedUsers.includes(user) ? (
-
- ) : null}
-
- ))}
-
-
-
-
- {selectedUsers.length > 0 ? (
-
- {selectedUsers.map((user) => (
-
- {user.name[0]}
-
- ))}
-
- ) : (
-
- Select users to add to this thread.
-
- )}
- {
- setOpen(false);
- }}
- >
- Continue
-
-
-
-
- >
- );
-}
diff --git a/apps/docs/src/components/docs/bento/cookie-settings.tsx b/apps/docs/src/components/docs/bento/cookie-settings.tsx
deleted file mode 100644
index 515542a8..00000000
--- a/apps/docs/src/components/docs/bento/cookie-settings.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-"use client";
-
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- Label,
- Switch,
-} from "ghost-ui";
-
-export function CardsCookieSettings() {
- return (
-
-
- Cookie Settings
- Manage your cookie settings here.
-
-
-
-
- Strictly Necessary
-
- These cookies are essential in order to use the website and use
- its features.
-
-
-
-
-
-
- Functional Cookies
-
- These cookies allow the website to provide personalized
- functionality.
-
-
-
-
-
-
- Performance Cookies
-
- These cookies help to improve the performance of the website.
-
-
-
-
-
-
-
- Save preferences
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/create-account.tsx b/apps/docs/src/components/docs/bento/create-account.tsx
deleted file mode 100644
index 236868cd..00000000
--- a/apps/docs/src/components/docs/bento/create-account.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-"use client";
-
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- Input,
- Label,
-} from "ghost-ui";
-import { Icons } from "@/components/docs/icons";
-
-export function CardsCreateAccount() {
- return (
-
-
- Create an account
-
- Enter your email below to create your account
-
-
-
-
-
-
- GitHub
-
-
-
- Google
-
-
-
-
-
-
-
-
- Or continue with
-
-
-
-
- Email
-
-
-
- Password
-
-
-
-
- Create account
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/data-table.tsx b/apps/docs/src/components/docs/bento/data-table.tsx
deleted file mode 100644
index 239bd1c7..00000000
--- a/apps/docs/src/components/docs/bento/data-table.tsx
+++ /dev/null
@@ -1,322 +0,0 @@
-"use client";
-
-import {
- ColumnDef,
- ColumnFiltersState,
- flexRender,
- getCoreRowModel,
- getFilteredRowModel,
- getPaginationRowModel,
- getSortedRowModel,
- SortingState,
- useReactTable,
- VisibilityState,
-} from "@tanstack/react-table";
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
- Checkbox,
- DropdownMenu,
- DropdownMenuCheckboxItem,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
- Input,
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "ghost-ui";
-import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react";
-import * as React from "react";
-
-const data: Payment[] = [
- {
- id: "m5gr84i9",
- amount: 316,
- status: "success",
- email: "ken99@example.com",
- },
- {
- id: "3u1reuv4",
- amount: 242,
- status: "success",
- email: "Abe45@example.com",
- },
- {
- id: "derv1ws0",
- amount: 837,
- status: "processing",
- email: "Monserrat44@example.com",
- },
- {
- id: "bhqecj4p",
- amount: 721,
- status: "failed",
- email: "carmella@example.com",
- },
-];
-
-export type Payment = {
- id: string;
- amount: number;
- status: "pending" | "processing" | "success" | "failed";
- email: string;
-};
-
-export const columns: ColumnDef[] = [
- {
- id: "select",
- header: ({ table }) => (
- table.toggleAllPageRowsSelected(!!value)}
- aria-label="Select all"
- />
- ),
- cell: ({ row }) => (
- row.toggleSelected(!!value)}
- aria-label="Select row"
- />
- ),
- enableSorting: false,
- enableHiding: false,
- },
- {
- accessorKey: "status",
- header: "Status",
- cell: ({ row }) => (
- {row.getValue("status")}
- ),
- },
- {
- accessorKey: "email",
- header: ({ column }) => {
- return (
- column.toggleSorting(column.getIsSorted() === "asc")}
- >
- Email
-
-
- );
- },
- cell: ({ row }) => {row.getValue("email")}
,
- },
- {
- accessorKey: "amount",
- header: () => Amount
,
- cell: ({ row }) => {
- const amount = parseFloat(row.getValue("amount"));
-
- // Format the amount as a dollar amount
- const formatted = new Intl.NumberFormat("en-US", {
- style: "currency",
- currency: "USD",
- }).format(amount);
-
- return (
- {formatted}
- );
- },
- },
- {
- id: "actions",
- enableHiding: false,
- cell: ({ row }) => {
- const payment = row.original;
-
- return (
-
-
-
- Open menu
-
-
-
-
- Actions
- navigator.clipboard.writeText(payment.id)}
- >
- Copy payment ID
-
-
- View customer
- View payment details
-
-
- );
- },
- },
-];
-
-export function CardsDataTable() {
- const [sorting, setSorting] = React.useState([]);
- const [columnFilters, setColumnFilters] = React.useState(
- [],
- );
- const [columnVisibility, setColumnVisibility] =
- React.useState({});
- const [rowSelection, setRowSelection] = React.useState({});
-
- const table = useReactTable({
- data,
- columns,
- onSortingChange: setSorting,
- onColumnFiltersChange: setColumnFilters,
- getCoreRowModel: getCoreRowModel(),
- getPaginationRowModel: getPaginationRowModel(),
- getSortedRowModel: getSortedRowModel(),
- getFilteredRowModel: getFilteredRowModel(),
- onColumnVisibilityChange: setColumnVisibility,
- onRowSelectionChange: setRowSelection,
- state: {
- sorting,
- columnFilters,
- columnVisibility,
- rowSelection,
- },
- });
-
- return (
-
-
- Payments
- Manage your payments.
-
-
-
-
- table.getColumn("email")?.setFilterValue(event.target.value)
- }
- className="max-w-sm"
- />
-
-
-
- Columns
-
-
-
- {table
- .getAllColumns()
- .filter((column) => column.getCanHide())
- .map((column) => {
- return (
-
- column.toggleVisibility(!!value)
- }
- >
- {column.id}
-
- );
- })}
-
-
-
-
-
-
- {table.getHeaderGroups().map((headerGroup) => (
-
- {headerGroup.headers.map((header) => {
- return (
-
- {header.isPlaceholder
- ? null
- : flexRender(
- header.column.columnDef.header,
- header.getContext(),
- )}
-
- );
- })}
-
- ))}
-
-
- {table.getRowModel().rows?.length ? (
- table.getRowModel().rows.map((row) => (
-
- {row.getVisibleCells().map((cell) => (
-
- {flexRender(
- cell.column.columnDef.cell,
- cell.getContext(),
- )}
-
- ))}
-
- ))
- ) : (
-
-
- No results.
-
-
- )}
-
-
-
-
-
- {table.getFilteredSelectedRowModel().rows.length} of{" "}
- {table.getFilteredRowModel().rows.length} row(s) selected.
-
-
- table.previousPage()}
- disabled={!table.getCanPreviousPage()}
- >
- Previous
-
- table.nextPage()}
- disabled={!table.getCanNextPage()}
- >
- Next
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/index.tsx b/apps/docs/src/components/docs/bento/index.tsx
deleted file mode 100644
index bf01514f..00000000
--- a/apps/docs/src/components/docs/bento/index.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-"use client";
-
-import { useIsMobile } from "ghost-ui";
-import { lazy, Suspense } from "react";
-import { CardsChat } from "@/components/docs/bento/chat";
-import { CardsCookieSettings } from "@/components/docs/bento/cookie-settings";
-import { CardsCreateAccount } from "@/components/docs/bento/create-account";
-import { CardsPaymentMethod } from "@/components/docs/bento/payment-method";
-import { CardsReportIssue } from "@/components/docs/bento/report-issue";
-import { CardsShare } from "@/components/docs/bento/share";
-import { CardsTeamMembers } from "@/components/docs/bento/team-members";
-
-const CardsStats = lazy(() =>
- import("@/components/docs/bento/stats").then((m) => ({
- default: m.CardsStats,
- })),
-);
-const CardsCalendar = lazy(() =>
- import("@/components/docs/bento/calendar").then((m) => ({
- default: m.CardsCalendar,
- })),
-);
-const CardsActivityGoal = lazy(() =>
- import("@/components/docs/bento/activity-goal").then((m) => ({
- default: m.CardsActivityGoal,
- })),
-);
-const CardsMetric = lazy(() =>
- import("@/components/docs/bento/metric").then((m) => ({
- default: m.CardsMetric,
- })),
-);
-const CardsDataTable = lazy(() =>
- import("@/components/docs/bento/data-table").then((m) => ({
- default: m.CardsDataTable,
- })),
-);
-
-function CalendarMetricGroup() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export function BentoDemo() {
- const isMobile = useIsMobile();
-
- return (
-
-
-
-
-
- {isMobile &&
}
-
-
-
- {!isMobile && (
- <>
-
-
-
-
- >
- )}
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/metric.tsx b/apps/docs/src/components/docs/bento/metric.tsx
deleted file mode 100644
index 17d3c52f..00000000
--- a/apps/docs/src/components/docs/bento/metric.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "ghost-ui";
-import { Line, LineChart } from "recharts";
-
-const data = [
- {
- average: 400,
- today: 240,
- },
- {
- average: 300,
- today: 139,
- },
- {
- average: 200,
- today: 980,
- },
- {
- average: 278,
- today: 390,
- },
- {
- average: 189,
- today: 480,
- },
- {
- average: 239,
- today: 380,
- },
- {
- average: 349,
- today: 430,
- },
-];
-
-const chartConfig = {
- today: {
- label: "Current Value",
- color: "hsl(var(--primary))",
- },
- average: {
- label: "Average Value",
- color: "hsl(var(--primary))",
- },
-} satisfies ChartConfig;
-
-export function CardsMetric() {
- return (
-
-
- Portfolio Value
-
- Your portfolio is performing above its 7-day average.
-
-
-
-
-
-
-
- } />
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/payment-amount.tsx b/apps/docs/src/components/docs/bento/payment-amount.tsx
deleted file mode 100644
index d2b84774..00000000
--- a/apps/docs/src/components/docs/bento/payment-amount.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-"use client";
-
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
-} from "ghost-ui";
-import { Minus, Plus } from "lucide-react";
-import * as React from "react";
-
-export function PaymentAmount() {
- const [amount, setAmount] = React.useState(350);
-
- function onClick(adjustment: number) {
- setAmount(Math.max(200, Math.min(400, amount + adjustment)));
- }
-
- return (
-
-
- Payment Amount
- Set your payment amount.
-
-
-
-
onClick(-10)}
- disabled={amount <= 200}
- >
-
- Decrease
-
-
-
onClick(10)}
- disabled={amount >= 400}
- >
-
- Increase
-
-
-
-
- Pay Now
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/payment-method.tsx b/apps/docs/src/components/docs/bento/payment-method.tsx
deleted file mode 100644
index 7df3c97e..00000000
--- a/apps/docs/src/components/docs/bento/payment-method.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-"use client";
-
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- Input,
- Label,
- RadioGroup,
- RadioGroupItem,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "ghost-ui";
-import { Icons } from "@/components/docs/icons";
-
-export function CardsPaymentMethod() {
- return (
-
-
- Payment Method
-
- Add a new payment method to your account.
-
-
-
-
-
-
-
-
-
- CashApp
-
-
-
-
- Name
-
-
-
- City
-
-
-
- Card number
-
-
-
-
- Expires
-
-
-
-
-
- January
- February
- March
- April
- May
- June
- July
- August
- September
- October
- November
- December
-
-
-
-
- Year
-
-
-
-
-
- {Array.from({ length: 10 }, (_, i) => (
-
- {new Date().getFullYear() + i}
-
- ))}
-
-
-
-
- CVC
-
-
-
-
-
- Continue
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/report-issue.tsx b/apps/docs/src/components/docs/bento/report-issue.tsx
deleted file mode 100644
index 4cdf6b29..00000000
--- a/apps/docs/src/components/docs/bento/report-issue.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-"use client";
-
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- Input,
- Label,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- Textarea,
-} from "ghost-ui";
-import * as React from "react";
-
-export function CardsReportIssue() {
- const id = React.useId();
-
- return (
-
-
- Report an issue
-
- What area are you having problems with?
-
-
-
-
-
- Area
-
-
-
-
-
- Team
- Billing
- Account
- Deployments
- Support
-
-
-
-
- Security Level
-
-
-
-
-
- Severity 1 (Highest)
- Severity 2
- Severity 3
- Severity 4 (Lowest)
-
-
-
-
-
- Subject
-
-
-
- Description
-
-
-
-
-
- Cancel
-
- Submit
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/share.tsx b/apps/docs/src/components/docs/bento/share.tsx
deleted file mode 100644
index 58569ab6..00000000
--- a/apps/docs/src/components/docs/bento/share.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-"use client";
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Button,
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
- Input,
- Label,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- Separator,
-} from "ghost-ui";
-
-export function CardsShare() {
- return (
-
-
- Share this document
-
- Anyone with the link can view this document.
-
-
-
-
-
- Link
-
-
- Copy Link
-
-
-
-
- People with access
-
-
-
-
-
- OM
-
-
-
- Olivia Martin
-
-
m@example.com
-
-
-
-
-
-
-
- Can edit
- Can view
-
-
-
-
-
-
- IN
-
-
-
- Isabella Nguyen
-
-
b@example.com
-
-
-
-
-
-
-
- Can edit
- Can view
-
-
-
-
-
-
- SD
-
-
-
- Sofia Davis
-
-
p@example.com
-
-
-
-
-
-
-
- Can edit
- Can view
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/stats.tsx b/apps/docs/src/components/docs/bento/stats.tsx
deleted file mode 100644
index 261808ac..00000000
--- a/apps/docs/src/components/docs/bento/stats.tsx
+++ /dev/null
@@ -1,215 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
-} from "ghost-ui";
-import { useState } from "react";
-import { Bar, BarChart, Line, LineChart, Tooltip } from "recharts";
-
-const data = [
- {
- revenue: 12500,
- subscription: 320,
- },
- {
- revenue: 15800,
- subscription: 380,
- },
- {
- revenue: 14200,
- subscription: 350,
- },
- {
- revenue: 16800,
- subscription: 420,
- },
- {
- revenue: 19200,
- subscription: 450,
- },
- {
- revenue: 17500,
- subscription: 410,
- },
- {
- revenue: 20300,
- subscription: 480,
- },
- {
- revenue: 22800,
- subscription: 520,
- },
-];
-
-const chartConfig = {
- revenue: {
- label: "Revenue",
- color: "hsl(var(--primary))",
- },
- subscription: {
- label: "Subscriptions",
- color: "hsl(var(--primary))",
- },
-} satisfies ChartConfig;
-
-interface CustomTooltipProps {
- active?: boolean;
- payload?: any[];
- label?: string;
- type?: "revenue" | "subscription";
-}
-
-const CustomTooltip = ({
- active,
- payload,
- label,
- type,
-}: CustomTooltipProps) => {
- if (active && payload && payload.length) {
- return (
-
-
- {type === "revenue" ? "$" : ""}
- {payload[0].value.toLocaleString()}
-
-
- );
- }
- return null;
-};
-
-export function CardsStats() {
- const [activeIndex, setActiveIndex] = useState(null);
-
- const handleMouseEnter = (props: any) => {
- if (props && typeof props.index === "number") {
- setActiveIndex(props.index);
- }
- };
-
- const handleMouseLeave = () => {
- setActiveIndex(null);
- };
-
- return (
-
-
-
- Total Revenue
-
-
-
-
-
- {
- const isActive = activeIndex === props.index;
- return (
-
- );
- }}
- onMouseEnter={handleMouseEnter}
- />
- }
- cursor={false}
- />
-
-
-
-
-
$15,231.89
-
- +20.1% from last month
-
-
-
-
-
-
- Subscriptions
-
-
-
-
-
- handleMouseEnter(data)}
- className={`transition-opacity duration-200 ${
- activeIndex !== null ? "opacity-60" : ""
- }`}
- />
- }
- cursor={{
- fill: "var(--border)",
- opacity: 0.1,
- }}
- />
-
-
-
-
-
+2350
-
- +180.1% from last month
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/bento/team-members.tsx b/apps/docs/src/components/docs/bento/team-members.tsx
deleted file mode 100644
index d2e2a2b9..00000000
--- a/apps/docs/src/components/docs/bento/team-members.tsx
+++ /dev/null
@@ -1,196 +0,0 @@
-"use client";
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Button,
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "ghost-ui";
-import { ChevronDown } from "lucide-react";
-
-export function CardsTeamMembers() {
- return (
-
-
- Team Members
-
- Invite your team members to collaborate.
-
-
-
-
-
-
- OM
-
-
-
Sofia Davis
-
m@example.com
-
-
-
-
-
- Owner
-
-
-
-
-
-
- No roles found.
-
-
- Viewer
-
- Can view and comment.
-
-
-
- Developer
-
- Can view, comment and edit.
-
-
-
- Billing
-
- Can view, comment and manage billing.
-
-
-
- Owner
-
- Admin-level access to all resources.
-
-
-
-
-
-
-
-
-
-
-
- JL
-
-
-
Jackson Lee
-
p@example.com
-
-
-
-
-
- Member
-
-
-
-
-
-
- No roles found.
-
-
- Viewer
-
- Can view and comment.
-
-
-
- Developer
-
- Can view, comment and edit.
-
-
-
- Billing
-
- Can view, comment and manage billing.
-
-
-
- Owner
-
- Admin-level access to all resources.
-
-
-
-
-
-
-
-
-
-
-
- IN
-
-
-
- Isabella Nguyen
-
-
i@example.com
-
-
-
-
-
- Member
-
-
-
-
-
-
- No roles found.
-
-
- Viewer
-
- Can view and comment.
-
-
-
- Developer
-
- Can view, comment and edit.
-
-
-
- Billing
-
- Can view, comment and manage billing.
-
-
-
- Owner
-
- Admin-level access to all resources.
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/callout.tsx b/apps/docs/src/components/docs/callout.tsx
index 629c9197..205fbbf7 100644
--- a/apps/docs/src/components/docs/callout.tsx
+++ b/apps/docs/src/components/docs/callout.tsx
@@ -1,4 +1,4 @@
-import { cn } from "ghost-ui";
+import { cn } from "@design-intelligence/vessel";
import type { ReactNode } from "react";
type Variant = "info" | "warning" | "wip";
diff --git a/apps/docs/src/components/docs/cli-help.tsx b/apps/docs/src/components/docs/cli-help.tsx
index 8f4be9f7..e944ff29 100644
--- a/apps/docs/src/components/docs/cli-help.tsx
+++ b/apps/docs/src/components/docs/cli-help.tsx
@@ -1,6 +1,6 @@
import manifest from "@/generated/cli-manifest.json";
-type ToolName = "ghost" | "ghost-fleet";
+type ToolName = "ghost";
interface CliHelpProps {
command: string;
diff --git a/apps/docs/src/components/docs/component-page-shell.tsx b/apps/docs/src/components/docs/component-page-shell.tsx
deleted file mode 100644
index 73ceaa55..00000000
--- a/apps/docs/src/components/docs/component-page-shell.tsx
+++ /dev/null
@@ -1,577 +0,0 @@
-"use client";
-
-import {
- Button,
- CodeBlock,
- CodeBlockActions,
- CodeBlockCopyButton,
- CodeBlockHeader,
- cn,
- useCopyToClipboard,
-} from "ghost-ui";
-import gsap from "gsap";
-import {
- Check as CheckIcon,
- Copy as CopyIcon,
- Download as DownloadIcon,
- ArrowLeft as NavigationBackIcon,
-} from "lucide-react";
-import { useEffect, useRef, useState } from "react";
-import { Link } from "react-router";
-import { DemoLoader, ExampleLoader } from "@/components/docs/demo-loader";
-import { ComponentErrorBoundary } from "@/components/docs/error-boundary";
-import { SectionWrapper } from "@/components/docs/wrappers";
-import type { ComponentDoc } from "@/lib/component-docs";
-import { ComponentEntry } from "@/lib/component-registry";
-import type { ComponentSpec } from "@/lib/component-source";
-
-// ── Spec Row ──
-
-function SpecRow({
- label,
- children,
- mono = false,
-}: {
- label: string;
- children: React.ReactNode;
- mono?: boolean;
-}) {
- return (
-
-
- {label}
-
-
- {children}
-
-
- );
-}
-
-// ── Variant Table ──
-
-function VariantTable({ variants }: { variants: ComponentSpec["variants"] }) {
- if (variants.length === 0) return null;
-
- return (
-
- {variants.map((v) => (
-
-
- {v.name}
- {v.defaultValue && (
-
- default: {v.defaultValue}
-
- )}
-
-
- {v.values.map((val) => (
-
- {val}
-
- ))}
-
-
- ))}
-
- );
-}
-
-// ── Main Shell ──
-
-export function ComponentPageShell({
- component,
- categoryName,
- demoSource,
- spec,
- prev,
- next,
- docs,
-}: {
- component: ComponentEntry;
- categoryName: string;
- demoSource: string | null;
- spec: ComponentSpec | null;
- prev: { slug: string; name: string } | null;
- next: { slug: string; name: string } | null;
- docs?: ComponentDoc;
-}) {
- const [activeTab, setActiveTab] = useState<"preview" | "source" | "demo">(
- "preview",
- );
- const { copyToClipboard, isCopied } = useCopyToClipboard();
- const shellRef = useRef(null);
-
- useEffect(() => {
- const ctx = gsap.context(() => {
- const tl = gsap.timeline({ defaults: { ease: "expo.out" } });
- const header = shellRef.current?.querySelector(".shell-header");
- const content = shellRef.current?.querySelector(".shell-content");
- if (header) {
- gsap.set(header, { y: 24, opacity: 0 });
- tl.to(header, { y: 0, opacity: 1, duration: 0.6 });
- }
- if (content) {
- gsap.set(content, { y: 16, opacity: 0 });
- tl.to(content, { y: 0, opacity: 1, duration: 0.5 }, "-=0.25");
- }
- }, shellRef);
- return () => ctx.revert();
- }, [component.slug]);
-
- const installCommand = `npx shadcn@latest add --registry https://block.github.io/ghost/r/registry.json ${component.slug}`;
-
- // Filter exports to only component names (capitalized), exclude variant exports
- const componentExports =
- spec?.exports.filter(
- (e) => /^[A-Z]/.test(e) && !e.toLowerCase().includes("variant"),
- ) ?? [];
-
- return (
- <>
- {/* ── Prev / Next (floating sides, xl+) ── */}
- {prev && (
-
-
-
-
-
- {prev.name}
-
-
- )}
- {next && (
-
-
-
-
-
- {next.name}
-
-
- )}
-
-
- {/* ── Header ── */}
-
-
-
-
-
-
-
- {categoryName}
-
-
-
-
-
- {component.name}
-
- {docs?.description && (
-
- {docs.description}
-
- )}
-
-
-
- {/* ── Install ── */}
-
-
-
- {installCommand}
-
- copyToClipboard(installCommand)}
- >
-
-
-
-
-
- {docs?.usage && (
-
-
-
-
- Usage
-
-
-
-
-
-
-
- )}
-
- {/* ── Tabs: Preview / Source / Demo Code ── */}
-
-
- {(
- [
- ["preview", "Preview"],
- ...(spec?.source ? [["source", "Source"]] : []),
- ...(demoSource ? [["demo", "Demo"]] : []),
- ] as [string, string][]
- ).map(([key, label]) => (
- setActiveTab(key as typeof activeTab)}
- className={cn(
- "px-3 py-2 text-sm font-medium transition-colors relative cursor-pointer",
- activeTab === key
- ? "text-foreground"
- : "text-muted-foreground hover:text-foreground",
- )}
- >
- {label}
- {activeTab === key && (
-
- )}
-
- ))}
-
-
-
- {activeTab === "preview" && (
-
-
-
- )}
-
- {activeTab === "source" && spec?.source && (
-
-
-
-
- {spec.filePath}
-
-
-
-
-
-
-
- )}
-
- {activeTab === "demo" && demoSource && (
-
-
-
-
- {component.slug}-demo.tsx
-
-
-
-
-
-
-
- )}
-
-
-
- {/* ── Spec Sheet ── */}
-
-
-
- Specification
-
-
-
-
- {/* Variants */}
- {spec && spec.variants.length > 0 && (
-
-
-
- )}
-
- {/* Sub-components */}
- {componentExports.length > 1 && (
-
-
- {componentExports.map((exp) => (
-
- {exp}
-
- ))}
-
-
- )}
-
- {/* Data slots */}
- {spec && spec.dataSlots.length > 0 && (
-
-
- {spec.dataSlots.map((slot) => (
-
- [data-slot="{slot}"]
-
- ))}
-
-
- )}
-
- {/* Registry dependencies */}
- {component.registryDependencies.length > 0 && (
-
-
- {component.registryDependencies.map((dep) => (
-
- {dep}
-
- ))}
-
-
- )}
-
- {/* npm Dependencies */}
- {component.dependencies.length > 0 && (
-
-
- {component.dependencies.map((dep) => (
-
- {dep}
-
- ))}
-
-
- )}
-
- {/* File path */}
- {spec?.filePath && (
-
-
- {spec.filePath}
-
-
- )}
-
-
-
- {docs && docs.props.length > 0 && (
-
-
-
- Props
-
-
-
- {docs.props.map((prop) => (
-
-
-
- {prop.type}
-
- {prop.default && (
-
- Default:{" "}
- {prop.default}
-
- )}
- {prop.description}
-
-
- ))}
-
-
- )}
-
- {docs && docs.composedWith.length > 0 && (
-
-
-
- Works With
-
-
-
-
- {docs.composedWith.map((slug) => (
-
- {slug}
-
- ))}
-
-
-
- )}
-
- {docs && docs.examples.length > 0 && (
-
-
- Examples
-
- {docs.examples.map((example) => (
-
-
-
{example.title}
- {example.description && (
-
- {example.description}
-
- )}
-
-
-
-
-
-
-
- ))}
-
- )}
-
- {/* ── Prev / Next (bottom, below xl) ── */}
-
- {prev ? (
-
-
-
-
- Previous
-
-
- {prev.name}
-
-
-
- ) : (
-
- )}
- {next ? (
-
-
- Next
-
- {next.name}
-
-
-
-
- ) : (
-
- )}
-
-
- {/* spacer for bottom nav hidden on xl */}
-
-
-
-
- >
- );
-}
diff --git a/apps/docs/src/components/docs/demo-loader.tsx b/apps/docs/src/components/docs/demo-loader.tsx
deleted file mode 100644
index b95c5bc1..00000000
--- a/apps/docs/src/components/docs/demo-loader.tsx
+++ /dev/null
@@ -1,536 +0,0 @@
-"use client";
-
-import { Skeleton } from "ghost-ui";
-import { lazy, Suspense, useMemo } from "react";
-
-const exampleModules = import.meta.glob<{ default: React.ComponentType }>(
- "/src/components/docs/examples/**/*.tsx",
-);
-
-const LoadingSkeleton = () => (
-
-
-
-
-);
-
-export function ExampleLoader({
- componentSlug,
- exampleName,
-}: {
- componentSlug: string;
- exampleName: string;
-}) {
- const path = `/src/components/docs/examples/${componentSlug}/${exampleName}.tsx`;
- const loader = exampleModules[path];
-
- const LazyComponent = useMemo(() => {
- if (!loader) return null;
- return lazy(loader);
- }, [loader]);
-
- if (!LazyComponent) return null;
-
- return (
- }>
-
-
- );
-}
-
-/* eslint-disable @typescript-eslint/no-explicit-any */
-const wrap = (imp: Promise, name: string) =>
- imp.then((m: any) => ({ default: m[name] }));
-
-const demos: Record> = {
- // ── Inputs ──
- button: lazy(() =>
- wrap(import("@/components/docs/primitives/button-demo"), "ButtonDemo"),
- ),
- input: lazy(() =>
- wrap(import("@/components/docs/primitives/input-demo"), "InputDemo"),
- ),
- textarea: lazy(() =>
- wrap(import("@/components/docs/primitives/textarea-demo"), "TextareaDemo"),
- ),
- select: lazy(() =>
- wrap(import("@/components/docs/primitives/select-demo"), "SelectDemo"),
- ),
- checkbox: lazy(() =>
- wrap(import("@/components/docs/primitives/checkbox-demo"), "CheckboxDemo"),
- ),
- "radio-group": lazy(() =>
- wrap(
- import("@/components/docs/primitives/radio-group-demo"),
- "RadioGroupDemo",
- ),
- ),
- slider: lazy(() =>
- wrap(import("@/components/docs/primitives/slider-demo"), "SliderDemo"),
- ),
- switch: lazy(() =>
- wrap(import("@/components/docs/primitives/switch-demo"), "SwitchDemo"),
- ),
- toggle: lazy(() =>
- wrap(import("@/components/docs/primitives/toggle-demo"), "ToggleDemo"),
- ),
- "toggle-group": lazy(() =>
- wrap(
- import("@/components/docs/primitives/toggle-group-demo"),
- "ToggleGroupDemo",
- ),
- ),
- combobox: lazy(() =>
- wrap(import("@/components/docs/primitives/combobox-demo"), "ComboboxDemo"),
- ),
- command: lazy(() =>
- wrap(import("@/components/docs/primitives/command-demo"), "CommandDemo"),
- ),
- "date-picker": lazy(() =>
- wrap(
- import("@/components/docs/primitives/date-picker-demo"),
- "DatePickerDemo",
- ),
- ),
- "input-otp": lazy(() =>
- wrap(import("@/components/docs/primitives/input-otp-demo"), "InputOTPDemo"),
- ),
- form: lazy(() =>
- wrap(import("@/components/docs/primitives/form-demo"), "FormDemo"),
- ),
- forms: lazy(() =>
- wrap(import("@/components/docs/primitives/forms-demo"), "FormsDemo"),
- ),
- label: lazy(() =>
- wrap(import("@/components/docs/primitives/label-demo"), "LabelDemo"),
- ),
-
- // ── Display ──
- card: lazy(() =>
- wrap(import("@/components/docs/primitives/card-demo"), "CardDemo"),
- ),
- badge: lazy(() =>
- wrap(import("@/components/docs/primitives/badge-demo"), "BadgeDemo"),
- ),
- avatar: lazy(() =>
- wrap(import("@/components/docs/primitives/avatar-demo"), "AvatarDemo"),
- ),
- carousel: lazy(() =>
- wrap(import("@/components/docs/primitives/carousel-demo"), "CarouselDemo"),
- ),
- "aspect-ratio": lazy(() =>
- wrap(
- import("@/components/docs/primitives/aspect-ratio-demo"),
- "AspectRatioDemo",
- ),
- ),
- "hover-card": lazy(() =>
- wrap(
- import("@/components/docs/primitives/hover-card-demo"),
- "HoverCardDemo",
- ),
- ),
- skeleton: lazy(() =>
- wrap(import("@/components/docs/primitives/skeleton-demo"), "SkeletonDemo"),
- ),
- separator: lazy(() =>
- wrap(
- import("@/components/docs/primitives/separator-demo"),
- "SeparatorDemo",
- ),
- ),
- image: lazy(() =>
- wrap(import("@/components/docs/ai-elements/image-demo"), "ImageDemo"),
- ),
-
- // ── Feedback ──
- alert: lazy(() =>
- wrap(import("@/components/docs/primitives/alert-demo"), "AlertDemo"),
- ),
- "alert-dialog": lazy(() =>
- wrap(
- import("@/components/docs/primitives/alert-dialog-demo"),
- "AlertDialogDemo",
- ),
- ),
- progress: lazy(() =>
- wrap(import("@/components/docs/primitives/progress-demo"), "ProgressDemo"),
- ),
- sonner: lazy(() =>
- wrap(import("@/components/docs/primitives/sonner-demo"), "SonnerDemo"),
- ),
-
- // ── Data ──
- table: lazy(() =>
- wrap(import("@/components/docs/primitives/table-demo"), "TableDemo"),
- ),
- chart: lazy(() =>
- wrap(import("@/components/docs/primitives/chart-demo"), "ChartDemo"),
- ),
- "chart-area": lazy(() =>
- wrap(
- import("@/components/docs/primitives/chart-area-demo"),
- "ChartAreaDemo",
- ),
- ),
- "chart-banded": lazy(() =>
- wrap(
- import("@/components/docs/primitives/chart-banded-demo"),
- "ChartBandedDemo",
- ),
- ),
- "chart-bar": lazy(() =>
- wrap(import("@/components/docs/primitives/chart-bar-demo"), "ChartBarDemo"),
- ),
- "chart-line": lazy(() =>
- wrap(
- import("@/components/docs/primitives/chart-line-demo"),
- "ChartLineDemo",
- ),
- ),
- "chart-pie": lazy(() =>
- wrap(import("@/components/docs/primitives/chart-pie-demo"), "ChartPieDemo"),
- ),
- "chart-posneg-bar": lazy(() =>
- wrap(
- import("@/components/docs/primitives/chart-posneg-bar-demo"),
- "ChartPosNegBarDemo",
- ),
- ),
- calendar: lazy(() =>
- wrap(import("@/components/docs/primitives/calendar-demo"), "CalendarDemo"),
- ),
-
- // ── Overlay ──
- dialog: lazy(() =>
- wrap(import("@/components/docs/primitives/dialog-demo"), "DialogDemo"),
- ),
- drawer: lazy(() =>
- wrap(import("@/components/docs/primitives/drawer-demo"), "DrawerDemo"),
- ),
- sheet: lazy(() =>
- wrap(import("@/components/docs/primitives/sheet-demo"), "SheetDemo"),
- ),
- popover: lazy(() =>
- wrap(import("@/components/docs/primitives/popover-demo"), "PopoverDemo"),
- ),
- tooltip: lazy(() =>
- wrap(import("@/components/docs/primitives/tooltip-demo"), "TooltipDemo"),
- ),
- "context-menu": lazy(() =>
- wrap(
- import("@/components/docs/primitives/context-menu-demo"),
- "ContextMenuDemo",
- ),
- ),
- "dropdown-menu": lazy(() =>
- wrap(
- import("@/components/docs/primitives/dropdown-menu-demo"),
- "DropdownMenuDemo",
- ),
- ),
- menubar: lazy(() =>
- wrap(import("@/components/docs/primitives/menubar-demo"), "MenubarDemo"),
- ),
-
- // ── Navigation ──
- breadcrumb: lazy(() =>
- wrap(
- import("@/components/docs/primitives/breadcrumb-demo"),
- "BreadcrumbDemo",
- ),
- ),
- tabs: lazy(() =>
- wrap(import("@/components/docs/primitives/tabs-demo"), "TabsDemo"),
- ),
- "navigation-menu": lazy(() =>
- wrap(
- import("@/components/docs/primitives/navigation-menu-demo"),
- "NavigationMenuDemo",
- ),
- ),
- pagination: lazy(() =>
- wrap(
- import("@/components/docs/primitives/pagination-demo"),
- "PaginationDemo",
- ),
- ),
- "scroll-area": lazy(() =>
- wrap(
- import("@/components/docs/primitives/scroll-area-demo"),
- "ScrollAreaDemo",
- ),
- ),
-
- // ── Layout ──
- accordion: lazy(() =>
- wrap(
- import("@/components/docs/primitives/accordion-demo"),
- "AccordionDemo",
- ),
- ),
- collapsible: lazy(() =>
- wrap(
- import("@/components/docs/primitives/collapsible-demo"),
- "CollapsibleDemo",
- ),
- ),
- resizable: lazy(() =>
- wrap(
- import("@/components/docs/primitives/resizable-demo"),
- "ResizableDemo",
- ),
- ),
-
- // ── Chat ──
- attachments: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/attachments-demo"),
- "AttachmentsDemo",
- ),
- ),
- "chain-of-thought": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/chain-of-thought-demo"),
- "ChainOfThoughtDemo",
- ),
- ),
- checkpoint: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/checkpoint-demo"),
- "CheckpointDemo",
- ),
- ),
- confirmation: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/confirmation-demo"),
- "ConfirmationDemo",
- ),
- ),
- context: lazy(() =>
- wrap(import("@/components/docs/ai-elements/context-demo"), "ContextDemo"),
- ),
- conversation: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/conversation-demo"),
- "ConversationDemo",
- ),
- ),
- "inline-citation": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/inline-citation-demo"),
- "InlineCitationDemo",
- ),
- ),
- message: lazy(() =>
- wrap(import("@/components/docs/ai-elements/message-demo"), "MessageDemo"),
- ),
- "model-selector": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/model-selector-demo"),
- "ModelSelectorDemo",
- ),
- ),
- "open-in-chat": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/open-in-chat-demo"),
- "OpenInChatDemo",
- ),
- ),
- plan: lazy(() =>
- wrap(import("@/components/docs/ai-elements/plan-demo"), "PlanDemo"),
- ),
- "prompt-input": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/prompt-input-demo"),
- "PromptInputDemo",
- ),
- ),
- queue: lazy(() =>
- wrap(import("@/components/docs/ai-elements/queue-demo"), "QueueDemo"),
- ),
- reasoning: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/reasoning-demo"),
- "ReasoningDemo",
- ),
- ),
- shimmer: lazy(() =>
- wrap(import("@/components/docs/ai-elements/shimmer-demo"), "ShimmerDemo"),
- ),
- sources: lazy(() =>
- wrap(import("@/components/docs/ai-elements/sources-demo"), "SourcesDemo"),
- ),
- suggestion: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/suggestion-demo"),
- "SuggestionDemo",
- ),
- ),
- task: lazy(() =>
- wrap(import("@/components/docs/ai-elements/task-demo"), "TaskDemo"),
- ),
- tool: lazy(() =>
- wrap(import("@/components/docs/ai-elements/tool-demo"), "ToolDemo"),
- ),
-
- // ── Code ──
- agent: lazy(() =>
- wrap(import("@/components/docs/ai-elements/agent-demo"), "AgentDemo"),
- ),
- artifact: lazy(() =>
- wrap(import("@/components/docs/ai-elements/artifact-demo"), "ArtifactDemo"),
- ),
- "code-block": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/code-block-demo"),
- "CodeBlockDemo",
- ),
- ),
- commit: lazy(() =>
- wrap(import("@/components/docs/ai-elements/commit-demo"), "CommitDemo"),
- ),
- "environment-variables": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/environment-variables-demo"),
- "EnvironmentVariablesDemo",
- ),
- ),
- "file-tree": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/file-tree-demo"),
- "FileTreeDemo",
- ),
- ),
- "jsx-preview": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/jsx-preview-demo"),
- "JsxPreviewDemo",
- ),
- ),
- "package-info": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/package-info-demo"),
- "PackageInfoDemo",
- ),
- ),
- sandbox: lazy(() =>
- wrap(import("@/components/docs/ai-elements/sandbox-demo"), "SandboxDemo"),
- ),
- "schema-display": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/schema-display-demo"),
- "SchemaDisplayDemo",
- ),
- ),
- snippet: lazy(() =>
- wrap(import("@/components/docs/ai-elements/snippet-demo"), "SnippetDemo"),
- ),
- "stack-trace": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/stack-trace-demo"),
- "StackTraceDemo",
- ),
- ),
- terminal: lazy(() =>
- wrap(import("@/components/docs/ai-elements/terminal-demo"), "TerminalDemo"),
- ),
- "test-results": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/test-results-demo"),
- "TestResultsDemo",
- ),
- ),
- "web-preview": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/web-preview-demo"),
- "WebPreviewDemo",
- ),
- ),
-
- // ── Voice ──
- "audio-player": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/audio-player-demo"),
- "AudioPlayerDemo",
- ),
- ),
- "mic-selector": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/mic-selector-demo"),
- "MicSelectorDemo",
- ),
- ),
- persona: lazy(() =>
- wrap(import("@/components/docs/ai-elements/persona-demo"), "PersonaDemo"),
- ),
- "speech-input": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/speech-input-demo"),
- "SpeechInputDemo",
- ),
- ),
- transcription: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/transcription-demo"),
- "TranscriptionDemo",
- ),
- ),
- "voice-selector": lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/voice-selector-demo"),
- "VoiceSelectorDemo",
- ),
- ),
-
- // ── Workflow ──
- canvas: lazy(() =>
- wrap(import("@/components/docs/ai-elements/canvas-demo"), "CanvasDemo"),
- ),
- connection: lazy(() =>
- wrap(
- import("@/components/docs/ai-elements/connection-demo"),
- "ConnectionDemo",
- ),
- ),
- controls: lazy(() =>
- wrap(import("@/components/docs/ai-elements/controls-demo"), "ControlsDemo"),
- ),
- edge: lazy(() =>
- wrap(import("@/components/docs/ai-elements/edge-demo"), "EdgeDemo"),
- ),
- node: lazy(() =>
- wrap(import("@/components/docs/ai-elements/node-demo"), "NodeDemo"),
- ),
- panel: lazy(() =>
- wrap(import("@/components/docs/ai-elements/panel-demo"), "PanelDemo"),
- ),
- toolbar: lazy(() =>
- wrap(import("@/components/docs/ai-elements/toolbar-demo"), "ToolbarDemo"),
- ),
-};
-
-export function DemoLoader({ name }: { name: string }) {
- const Demo = demos[name];
-
- if (!Demo) {
- return (
-
- Component “{name}” not found
-
- );
- }
-
- return (
-
-
-
-
- }
- >
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/doc-prose.tsx b/apps/docs/src/components/docs/doc-prose.tsx
index e38e9120..886f9605 100644
--- a/apps/docs/src/components/docs/doc-prose.tsx
+++ b/apps/docs/src/components/docs/doc-prose.tsx
@@ -1,4 +1,4 @@
-import { cn } from "ghost-ui";
+import { cn } from "@design-intelligence/vessel";
import type { ComponentProps } from "react";
export function DocProse({
diff --git a/apps/docs/src/components/docs/dock.tsx b/apps/docs/src/components/docs/dock.tsx
index a6cae6ee..d07e7964 100644
--- a/apps/docs/src/components/docs/dock.tsx
+++ b/apps/docs/src/components/docs/dock.tsx
@@ -12,7 +12,7 @@ import {
TooltipContent,
TooltipTrigger,
useTheme,
-} from "ghost-ui";
+} from "@design-intelligence/vessel";
import type { LucideIcon } from "lucide-react";
import {
BookOpen,
@@ -224,44 +224,35 @@ export function Dock() {
CLI Reference
-
-
-
{
- navigate("/tools/scan");
+ navigate("/docs/checks-and-review");
setSearchOpen(false);
}}
>
-
- ghost scan
-
- {
- navigate("/tools/drift");
- setSearchOpen(false);
- }}
- >
-
- ghost review
+
+ Checks And Review
+
+
+
{
- navigate("/tools/fleet");
+ navigate("/tools/scan");
setSearchOpen(false);
}}
>
- ghost-fleet
+ ghost gather
{
- navigate("/tools/ui");
+ navigate("/tools/drift");
setSearchOpen(false);
}}
>
- ghost-ui
+ ghost review
diff --git a/apps/docs/src/components/docs/docs-page-layout.tsx b/apps/docs/src/components/docs/docs-page-layout.tsx
index 1c2449fa..ae387ebb 100644
--- a/apps/docs/src/components/docs/docs-page-layout.tsx
+++ b/apps/docs/src/components/docs/docs-page-layout.tsx
@@ -1,6 +1,6 @@
"use client";
-import { cn } from "ghost-ui";
+import { cn } from "@design-intelligence/vessel";
import type { ReactNode } from "react";
/**
diff --git a/apps/docs/src/components/docs/examples/code-block/with-diff.tsx b/apps/docs/src/components/docs/examples/code-block/with-diff.tsx
deleted file mode 100644
index c59240e0..00000000
--- a/apps/docs/src/components/docs/examples/code-block/with-diff.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import {
- CodeBlock,
- CodeBlockActions,
- CodeBlockCopyButton,
- CodeBlockHeader,
- CodeBlockLanguageSelector,
- CodeBlockLanguageSelectorContent,
- CodeBlockLanguageSelectorItem,
- CodeBlockLanguageSelectorTrigger,
- CodeBlockLanguageSelectorValue,
-} from "ghost-ui";
-import { useState } from "react";
-
-const snippets: Record<
- string,
- { code: string; language: "typescript" | "python" | "rust" }
-> = {
- typescript: {
- code: `function fibonacci(n: number): number {
- if (n <= 1) return n;
- return fibonacci(n - 1) + fibonacci(n - 2);
-}
-
-console.log(fibonacci(10)); // 55`,
- language: "typescript",
- },
- python: {
- code: `def fibonacci(n: int) -> int:
- if n <= 1:
- return n
- return fibonacci(n - 1) + fibonacci(n - 2)
-
-print(fibonacci(10)) # 55`,
- language: "python",
- },
- rust: {
- code: `fn fibonacci(n: u32) -> u32 {
- if n <= 1 {
- return n;
- }
- fibonacci(n - 1) + fibonacci(n - 2)
-}
-
-fn main() {
- println!("{}", fibonacci(10)); // 55
-}`,
- language: "rust",
- },
-};
-
-export default function CodeBlockWithDiff() {
- const [lang, setLang] = useState("typescript");
- const snippet = snippets[lang];
-
- return (
-
-
-
-
-
-
-
-
-
- TypeScript
-
-
- Python
-
-
- Rust
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/examples/conversation/with-messages.tsx b/apps/docs/src/components/docs/examples/conversation/with-messages.tsx
deleted file mode 100644
index a7f1f2c9..00000000
--- a/apps/docs/src/components/docs/examples/conversation/with-messages.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import {
- Conversation,
- ConversationContent,
- ConversationScrollButton,
- Message,
- MessageContent,
- MessageResponse,
-} from "ghost-ui";
-
-const messages = [
- { id: "1", role: "user" as const, text: "What is TypeScript?" },
- {
- id: "2",
- role: "assistant" as const,
- text: "TypeScript is a strongly-typed superset of JavaScript developed by Microsoft. It adds optional static type checking, interfaces, enums, and other features that help catch errors at compile time rather than at runtime.",
- },
- { id: "3", role: "user" as const, text: "How does it compare to Flow?" },
- {
- id: "4",
- role: "assistant" as const,
- text: "Both TypeScript and Flow add static types to JavaScript, but they differ in key ways:\n\n- **Adoption**: TypeScript has much wider community adoption and tooling support.\n- **Type system**: TypeScript uses a structural type system; Flow also uses structural typing but with some nominal typing features.\n- **Tooling**: TypeScript ships its own compiler (`tsc`), while Flow relies on Babel for compilation.\n- **Ecosystem**: TypeScript has DefinitelyTyped with type definitions for thousands of packages.",
- },
-];
-
-export default function ConversationWithMessages() {
- return (
-
-
-
- {messages.map((msg) => (
-
-
- {msg.role === "assistant" ? (
- {msg.text}
- ) : (
- {msg.text}
- )}
-
-
- ))}
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/examples/message/streaming.tsx b/apps/docs/src/components/docs/examples/message/streaming.tsx
deleted file mode 100644
index eb2f82d8..00000000
--- a/apps/docs/src/components/docs/examples/message/streaming.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Message, MessageContent, MessageResponse, Shimmer } from "ghost-ui";
-
-export default function MessageStreaming() {
- return (
-
-
-
- Explain quantum entanglement in simple terms.
-
-
-
-
-
-
- {`Quantum entanglement is when two particles become linked so that measuring one instantly affects the other, no matter how far apart they are. Einstein called it "spooky action at a distance."`}
-
-
- Generating...
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/examples/message/with-actions.tsx b/apps/docs/src/components/docs/examples/message/with-actions.tsx
deleted file mode 100644
index b6169642..00000000
--- a/apps/docs/src/components/docs/examples/message/with-actions.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import {
- Message,
- MessageAction,
- MessageActions,
- MessageContent,
- MessageResponse,
-} from "ghost-ui";
-import {
- CopyIcon,
- RefreshCwIcon,
- ThumbsDownIcon,
- ThumbsUpIcon,
-} from "lucide-react";
-
-export default function MessageWithActions() {
- return (
-
-
-
- How do I center a div?
-
-
-
-
-
-
- {`You can center a div using **flexbox**:\n\n\`\`\`css\n.parent {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\`\`\`\n\nOr use **grid**:\n\n\`\`\`css\n.parent {\n display: grid;\n place-items: center;\n}\n\`\`\``}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/examples/prompt-input/with-attachments.tsx b/apps/docs/src/components/docs/examples/prompt-input/with-attachments.tsx
deleted file mode 100644
index 549ee0ee..00000000
--- a/apps/docs/src/components/docs/examples/prompt-input/with-attachments.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import {
- PromptInput,
- PromptInputActionAddAttachments,
- PromptInputFooter,
- PromptInputSubmit,
- PromptInputTextarea,
- PromptInputTools,
-} from "ghost-ui";
-
-export default function PromptInputWithAttachments() {
- return (
-
-
{
- console.log("Submitted:", msg);
- }}
- >
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/external-link.tsx b/apps/docs/src/components/docs/external-link.tsx
index ae989157..ae739801 100644
--- a/apps/docs/src/components/docs/external-link.tsx
+++ b/apps/docs/src/components/docs/external-link.tsx
@@ -1,4 +1,4 @@
-import { cn } from "ghost-ui";
+import { cn } from "@design-intelligence/vessel";
import { ArrowUpRight } from "lucide-react";
import { ComponentProps } from "react";
diff --git a/apps/docs/src/components/docs/foundations/colors.tsx b/apps/docs/src/components/docs/foundations/colors.tsx
deleted file mode 100644
index b2e63f06..00000000
--- a/apps/docs/src/components/docs/foundations/colors.tsx
+++ /dev/null
@@ -1,444 +0,0 @@
-export function ColorsDemos() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Semantic Colors */}
-
-
- {/* Border Colors */}
-
-
- {/* Text Colors */}
-
-
- text
-
-
-
-
hello world
-
text default
-
-
-
hello world
-
text alt
-
-
-
hello world
-
text muted
-
-
-
- hello world
-
-
text inverse
-
-
-
hello world
-
text danger
-
-
-
hello world
-
text success
-
-
-
hello world
-
text warning
-
-
-
hello world
-
text info
-
-
-
hello world
-
text accent
-
-
-
-
- {/* Dark Surfaces */}
-
-
- dark surface
-
-
-
-
-
-
- surface dark muted
-
-
-
-
-
-
- surface dark border
-
-
-
-
-
-
- {/* Chart Colors */}
-
-
- {/* Shadows */}
-
-
- {/* Radii */}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/foundations/typography.tsx b/apps/docs/src/components/docs/foundations/typography.tsx
deleted file mode 100644
index 17c8b743..00000000
--- a/apps/docs/src/components/docs/foundations/typography.tsx
+++ /dev/null
@@ -1,297 +0,0 @@
-export function TypographyDemos() {
- return (
-
- {/* Heading Hierarchy */}
-
-
- Headings
-
-
-
-
- Display
-
-
- Display / 64–96px / Black (900) /-0.05em / 0.88 lh
-
-
-
-
- Section heading
-
-
- Section / 44–64px / Bold (700) /-0.035em / 0.95 lh
-
-
-
-
- Subsection heading
-
-
- Sub / 28–40px / Bold (700) /-0.02em / 1.0 lh
-
-
-
-
- Card heading
-
-
- Card / 20–28px / Semibold (600) /-0.01em / 1.1 lh
-
-
-
-
-
- {/* Labels */}
-
-
- Labels
-
-
-
-
-
- Culture
-
-
- The Future of Open Design
-
-
-
-
- Design · March 2026
-
-
- Systems That Scale
-
-
-
- 11px / Semibold (600) / uppercase / +0.12em tracking
-
-
-
-
-
- {/* Pull Quotes */}
-
-
- Pull Quotes
-
-
-
-
- The magic that happens when you work in the open — co-creation,
- co-celebration.
-
-
- Pull quote / 1.5–2.5rem / Light (300) / -0.02em / 1.3 lh
-
-
-
-
-
- {/* Type Scale */}
-
-
- Type Scale
-
-
-
-
- Open source design: all for one, one for all.
-
-
9XL / Light
-
-
-
- AI will not replace your job. Humans that use AI will.
-
-
8XL / Light
-
-
-
- Leave it cleaner than you found it.
-
-
7XL / Light
-
-
-
- The magic that happens when you work in the open.
-
-
6XL / Light
-
-
-
- Actual open AI. Co-creation, co-celebration.
-
-
5XL / Light
-
-
-
- Work hard smarter, play hard in the grass.
-
-
4XL / Light
-
-
-
- The first rule of open source design is you talk about open source
- design
-
-
3XL / Light
-
-
-
- Open source design: all for one, one for all.
-
-
2XL / Light
-
-
-
- The magic that happens when you work in the open.
-
-
XL / Light
-
-
-
- Leave it cleaner than you found it.
-
-
LG / Light
-
-
-
- Work hard smarter, play hard in the grass.
-
-
BASE / Light
-
-
-
- Actual open AI. Co-creation, co-celebration.
-
-
SM / Light
-
-
-
- AI will not replace your job. Humans that use AI will.
-
-
XS / Light
-
-
-
-
- {/* Font Weights */}
-
-
- Weights
-
-
-
-
The quick brown fox
-
300 / Light
-
-
-
The quick brown fox
-
400 / Regular
-
-
-
The quick brown fox
-
500 / Medium
-
-
-
The quick brown fox
-
600 / Semibold
-
-
-
The quick brown fox
-
700 / Bold
-
-
-
The quick brown fox
-
900 / Black
-
-
-
-
- {/* Font Families */}
-
-
- Families
-
-
-
-
- The quick brown fox jumps over the lazy dog
-
-
font-sans
-
-
-
- The quick brown fox jumps over the lazy dog
-
-
font-display
-
-
-
- Geist Mono — The quick brown fox
-
-
font-mono
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/hero.tsx b/apps/docs/src/components/docs/hero.tsx
index 93bf3a78..892eb842 100644
--- a/apps/docs/src/components/docs/hero.tsx
+++ b/apps/docs/src/components/docs/hero.tsx
@@ -28,7 +28,7 @@ export function Hero() {
return (
<>
- {/* Concentric circles — fixed backdrop, persists through page scroll */}
+ {/* Concentric circles: fixed backdrop, persists through page scroll */}
{[3, 4, 5].map((i) => {
const size = Math.pow(i, 1.6) * 12;
@@ -61,6 +61,10 @@ export function Hero() {
>
Ghost
+
+ Your brand, packed for agents: a steering packet they read before
+ they make anything. Design authority without a center.
+
>
diff --git a/apps/docs/src/components/docs/open-in-v0-button.tsx b/apps/docs/src/components/docs/open-in-v0-button.tsx
deleted file mode 100644
index 5f411973..00000000
--- a/apps/docs/src/components/docs/open-in-v0-button.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Button, cn } from "ghost-ui";
-
-export function OpenInV0Button({
- name,
- className,
-}: { name: string } & React.ComponentProps) {
- return (
-
-
- Open in{" "}
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/page-header.tsx b/apps/docs/src/components/docs/page-header.tsx
index 65b06ffe..826f5e32 100644
--- a/apps/docs/src/components/docs/page-header.tsx
+++ b/apps/docs/src/components/docs/page-header.tsx
@@ -1,4 +1,4 @@
-import { cn } from "ghost-ui";
+import { cn } from "@design-intelligence/vessel";
import { ComponentProps } from "react";
function PageHeader({
diff --git a/apps/docs/src/components/docs/primitives/accordion-demo.tsx b/apps/docs/src/components/docs/primitives/accordion-demo.tsx
deleted file mode 100644
index 3d1a4d93..00000000
--- a/apps/docs/src/components/docs/primitives/accordion-demo.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from "ghost-ui";
-
-export function AccordionDemo() {
- return (
-
-
-
- Is it accessible?
-
- Yes. It adheres to the WAI-ARIA design pattern.
-
-
-
- Is it styled?
-
- Yes. It comes with default styles that matches the other
- components' aesthetic.
-
-
-
- Is it animated?
-
- Yes. It's animated by default, but you can disable it if you
- prefer.
-
-
-
-
-
-
- What are the key considerations when implementing a comprehensive
- enterprise-level authentication system?
-
-
- Implementing a robust enterprise authentication system requires
- careful consideration of multiple factors. This includes secure
- password hashing and storage, multi-factor authentication (MFA)
- implementation, session management, OAuth2 and SSO integration,
- regular security audits, rate limiting to prevent brute force
- attacks, and maintaining detailed audit logs. Additionally,
- you'll need to consider scalability, performance impact, and
- compliance with relevant data protection regulations such as GDPR or
- HIPAA.
-
-
-
-
- How does modern distributed system architecture handle eventual
- consistency and data synchronization across multiple regions?
-
-
- Modern distributed systems employ various strategies to maintain
- data consistency across regions. This often involves using
- techniques like CRDT (Conflict-Free Replicated Data Types), vector
- clocks, and gossip protocols. Systems might implement event sourcing
- patterns, utilize message queues for asynchronous updates, and
- employ sophisticated conflict resolution strategies. Popular
- solutions like Amazon's DynamoDB and Google's Spanner
- demonstrate different approaches to solving these challenges,
- balancing between consistency, availability, and partition tolerance
- as described in the CAP theorem.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/alert-demo.tsx b/apps/docs/src/components/docs/primitives/alert-demo.tsx
deleted file mode 100644
index 4690fd05..00000000
--- a/apps/docs/src/components/docs/primitives/alert-demo.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { Alert, AlertDescription, AlertTitle, Button } from "ghost-ui";
-import {
- AlertCircleIcon,
- BookmarkCheckIcon,
- CheckCircle2Icon,
- GiftIcon,
- PopcornIcon,
- ShieldAlertIcon,
-} from "lucide-react";
-
-export function AlertDemo() {
- return (
-
-
-
- Success! Your changes have been saved
-
- This is an alert with icon, title and description.
-
-
-
- Heads up!
-
- This one has an icon and a description only. No title.
-
-
-
-
- This one has a description only. No title. No icon.
-
-
-
-
- Let's try one with icon and title.
-
-
-
-
- This is a very long alert title that demonstrates how the component
- handles extended text content and potentially wraps across multiple
- lines
-
-
-
-
-
- This is a very long alert description that demonstrates how the
- component handles extended text content and potentially wraps across
- multiple lines
-
-
-
-
-
- This is an extremely long alert title that spans multiple lines to
- demonstrate how the component handles very lengthy headings while
- maintaining readability and proper text wrapping behavior
-
-
- This is an equally long description that contains detailed information
- about the alert. It shows how the component can accommodate extensive
- content while preserving proper spacing, alignment, and readability
- across different screen sizes and viewport widths. This helps ensure
- the user experience remains consistent regardless of the content
- length.
-
-
-
-
- Something went wrong!
-
- Your session has expired. Please log in again.
-
-
-
-
- Unable to process your payment.
-
- Please verify your billing information and try again.
-
- Check your card details
- Ensure sufficient funds
- Verify billing address
-
-
-
-
-
-
- The selected emails have been marked as spam.
-
-
- Undo
-
-
-
-
- Plot Twist: This Alert is Actually Amber!
-
- This one has custom colors for light and dark mode.
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/alert-dialog-demo.tsx b/apps/docs/src/components/docs/primitives/alert-dialog-demo.tsx
deleted file mode 100644
index a37afac5..00000000
--- a/apps/docs/src/components/docs/primitives/alert-dialog-demo.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
- AlertDialogTrigger,
- Button,
-} from "ghost-ui";
-
-export function AlertDialogDemo() {
- return (
-
-
- Show Dialog
-
-
-
- Are you absolutely sure?
-
- This action cannot be undone. This will permanently delete your
- account and remove your data from our servers.
-
-
-
- Cancel
- Continue
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/aspect-ratio-demo.tsx b/apps/docs/src/components/docs/primitives/aspect-ratio-demo.tsx
deleted file mode 100644
index 3d9b838d..00000000
--- a/apps/docs/src/components/docs/primitives/aspect-ratio-demo.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { AspectRatio } from "ghost-ui";
-
-export function AspectRatioDemo() {
- return (
-
-
-
- 16:9
-
-
-
-
- 1:1
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/avatar-demo.tsx b/apps/docs/src/components/docs/primitives/avatar-demo.tsx
deleted file mode 100644
index bd40cc94..00000000
--- a/apps/docs/src/components/docs/primitives/avatar-demo.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import { Avatar, AvatarFallback, AvatarImage } from "ghost-ui";
-
-export function AvatarDemo() {
- return (
-
-
-
- NK
-
-
- NK
-
-
-
- NK
-
-
-
- SM
-
-
-
-
- NK
-
-
-
- BA
-
-
-
- SM
-
-
-
-
-
- NK
-
-
-
- BA
-
-
-
- SM
-
-
-
-
-
- NK
-
-
-
- BA
-
-
-
- SM
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/badge-demo.tsx b/apps/docs/src/components/docs/primitives/badge-demo.tsx
deleted file mode 100644
index d4b55b26..00000000
--- a/apps/docs/src/components/docs/primitives/badge-demo.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Badge } from "ghost-ui";
-import { AlertCircleIcon, ArrowRightIcon, CheckIcon } from "lucide-react";
-
-export function BadgeDemo() {
- return (
-
-
-
Badge
-
Secondary
-
Destructive
-
Outline
-
-
- Badge
-
-
-
- Alert
-
-
- 8
-
-
- 99
-
-
- 20+
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/breadcrumb-demo.tsx b/apps/docs/src/components/docs/primitives/breadcrumb-demo.tsx
deleted file mode 100644
index 4ca09c32..00000000
--- a/apps/docs/src/components/docs/primitives/breadcrumb-demo.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import {
- Breadcrumb,
- BreadcrumbEllipsis,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbList,
- BreadcrumbPage,
- BreadcrumbSeparator,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "ghost-ui";
-
-export function BreadcrumbDemo() {
- return (
-
-
-
- Home
-
-
-
-
-
-
- Toggle menu
-
-
- Documentation
- Themes
- GitHub
-
-
-
-
-
- Components
-
-
-
- Breadcrumb
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/button-demo.tsx b/apps/docs/src/components/docs/primitives/button-demo.tsx
deleted file mode 100644
index 2e21d1fa..00000000
--- a/apps/docs/src/components/docs/primitives/button-demo.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-import { Button } from "ghost-ui";
-import { ArrowRightIcon, Loader2Icon, SendIcon } from "lucide-react";
-
-export function ButtonDemo() {
- return (
-
-
-
Button
-
-
-
-
Outline
-
Ghost
-
-
-
-
Destructive
-
Secondary
-
Link
-
- Send
-
-
-
-
-
- Learn More
-
-
-
- Please wait
-
-
-
-
Small
-
-
-
-
- Outline
-
-
- Ghost
-
-
-
-
-
- Destructive
-
-
- Secondary
-
-
- Link
-
-
- Send
-
-
-
-
-
- Learn More
-
-
-
- Please wait
-
-
-
-
Large
-
-
-
-
- Outline
-
-
- Ghost
-
-
-
-
-
- Destructive
-
-
- Secondary
-
-
- Link
-
-
- Send
-
-
-
-
-
- Learn More
-
-
-
- Please wait
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/calendar-demo.tsx b/apps/docs/src/components/docs/primitives/calendar-demo.tsx
deleted file mode 100644
index 9d4d03d0..00000000
--- a/apps/docs/src/components/docs/primitives/calendar-demo.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-"use client";
-
-import { addDays } from "date-fns";
-import { Calendar } from "ghost-ui";
-import * as React from "react";
-import { type DateRange } from "react-day-picker";
-
-export function CalendarDemo() {
- const [date, setDate] = React.useState(new Date());
- const [dateRange, setDateRange] = React.useState({
- from: new Date(new Date().getFullYear(), 0, 12),
- to: addDays(new Date(new Date().getFullYear(), 0, 12), 30),
- });
- const [range, setRange] = React.useState({
- from: new Date(new Date().getFullYear(), 0, 12),
- to: addDays(new Date(new Date().getFullYear(), 0, 12), 50),
- });
-
- return (
-
-
- date > new Date() || date < new Date("1900-01-01")}
- className="rounded-md border"
- />
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/card-demo.tsx b/apps/docs/src/components/docs/primitives/card-demo.tsx
deleted file mode 100644
index 05974cb0..00000000
--- a/apps/docs/src/components/docs/primitives/card-demo.tsx
+++ /dev/null
@@ -1,187 +0,0 @@
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Badge,
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- Input,
- Label,
-} from "ghost-ui";
-import { BathIcon, BedIcon, LandPlotIcon } from "lucide-react";
-export function CardDemo() {
- return (
-
-
-
- Login to your account
-
- Enter your email below to login to your account
-
-
-
-
-
-
-
- Login
-
-
- Login with Google
-
-
-
-
-
-
- Meeting Notes
-
- Transcript from the meeting with the client.
-
-
-
-
- Client requested dashboard redesign with focus on mobile
- responsiveness.
-
-
- New analytics widgets for daily/weekly metrics
- Simplified navigation menu
- Dark mode support
- Timeline: 6 weeks
- Follow-up meeting scheduled for next Tuesday
-
-
-
-
-
-
- NK
-
-
-
- BA
-
-
-
- SM
-
-
-
-
-
-
- Is this an image?
- This is a card with an image.
-
-
-
-
-
-
- 4
-
-
- 2
-
-
- 350m²
-
- $135,000
-
-
-
-
- Content Only
-
-
-
- Header Only
-
- This is a card with a header and a description.
-
-
-
-
-
- Header and Content
-
- This is a card with a header and a content.
-
-
- Content
-
-
- Footer Only
-
-
-
- Header + Footer
-
- This is a card with a header and a footer.
-
-
- Footer
-
-
- Content
- Footer
-
-
-
- Header + Footer
-
- This is a card with a header and a footer.
-
-
- Content
- Footer
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/carousel-demo.tsx b/apps/docs/src/components/docs/primitives/carousel-demo.tsx
deleted file mode 100644
index 6402d64e..00000000
--- a/apps/docs/src/components/docs/primitives/carousel-demo.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import {
- Card,
- CardContent,
- Carousel,
- CarouselContent,
- CarouselItem,
- CarouselNext,
- CarouselPrevious,
-} from "ghost-ui";
-import * as React from "react";
-
-export function CarouselDemo() {
- return (
-
-
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
-
-
-
- {index + 1}
-
-
-
-
-
- ))}
-
-
-
-
-
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
-
-
-
- {index + 1}
-
-
-
-
-
- ))}
-
-
-
-
-
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
-
-
-
- {index + 1}
-
-
-
-
-
- ))}
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-area-demo.tsx b/apps/docs/src/components/docs/primitives/chart-area-demo.tsx
deleted file mode 100644
index 45bf4aed..00000000
--- a/apps/docs/src/components/docs/primitives/chart-area-demo.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "ghost-ui";
-import { TrendingUp } from "lucide-react";
-import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
-
-export const description = "A simple area chart";
-
-const chartData = [
- { month: "January", desktop: 186 },
- { month: "February", desktop: 305 },
- { month: "March", desktop: 237 },
- { month: "April", desktop: 73 },
- { month: "May", desktop: 209 },
- { month: "June", desktop: 214 },
-];
-
-const chartConfig = {
- desktop: {
- label: "Desktop",
- color: "var(--chart-1)",
- },
-} satisfies ChartConfig;
-
-export function ChartAreaDemo() {
- return (
-
-
- Area Chart
-
- Showing total visitors for the last 6 months
-
-
-
-
-
-
- value.slice(0, 3)}
- />
- }
- />
-
-
-
-
-
-
-
-
- Trending up by 5.2% this month
-
-
- January - June 2024
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-banded-demo.tsx b/apps/docs/src/components/docs/primitives/chart-banded-demo.tsx
deleted file mode 100644
index 47ff8ed4..00000000
--- a/apps/docs/src/components/docs/primitives/chart-banded-demo.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "ghost-ui";
-import { TrendingUp } from "lucide-react";
-import { ComposedChart, Line, XAxis, YAxis } from "recharts";
-
-export const description = "A banded chart showing range";
-
-const chartData = [
- { month: "Jan", upper: 86, lower: 23 },
- { month: "Feb", upper: 105, lower: 45 },
- { month: "Mar", upper: 137, lower: 58 },
- { month: "Apr", upper: 173, lower: 68 },
- { month: "May", upper: 109, lower: 45 },
- { month: "Jun", upper: 144, lower: 67 },
-];
-
-const chartConfig = {
- upper: {
- label: "Upper Band",
- color: "var(--chart-1)",
- },
- lower: {
- label: "Lower Band",
- color: "var(--chart-2)",
- },
-} satisfies ChartConfig;
-
-export function ChartBandedDemo() {
- return (
-
-
- Banded Chart
- Showing range variations over time
-
-
-
-
-
-
-
-
- }
- />
-
-
-
-
-
-
-
- Range increased by 12%
-
-
- January - June 2024
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-bar-demo.tsx b/apps/docs/src/components/docs/primitives/chart-bar-demo.tsx
deleted file mode 100644
index 1212bd01..00000000
--- a/apps/docs/src/components/docs/primitives/chart-bar-demo.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "ghost-ui";
-import { TrendingUp } from "lucide-react";
-import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
-
-export const description = "A multiple bar chart";
-
-const chartData = [
- { month: "January", desktop: 186, mobile: 80 },
- { month: "February", desktop: 305, mobile: 200 },
- { month: "March", desktop: 237, mobile: 120 },
- { month: "April", desktop: 73, mobile: 190 },
- { month: "May", desktop: 209, mobile: 130 },
- { month: "June", desktop: 214, mobile: 140 },
-];
-
-const chartConfig = {
- desktop: {
- label: "Desktop",
- color: "var(--chart-1)",
- },
- mobile: {
- label: "Mobile",
- color: "var(--chart-2)",
- },
-} satisfies ChartConfig;
-
-export function ChartBarDemo() {
- return (
-
-
- Bar Chart - Multiple
- January - June 2024
-
-
-
-
-
- value.slice(0, 3)}
- />
- }
- />
-
-
-
-
-
-
-
- Trending up by 5.2% this month
-
-
- Showing total visitors for the last 6 months
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-bar-mixed.tsx b/apps/docs/src/components/docs/primitives/chart-bar-mixed.tsx
deleted file mode 100644
index 05f23595..00000000
--- a/apps/docs/src/components/docs/primitives/chart-bar-mixed.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "ghost-ui";
-import { TrendingUp } from "lucide-react";
-import { Bar, BarChart, XAxis, YAxis } from "recharts";
-
-export const description = "A mixed bar chart";
-
-const chartData = [
- { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
- { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
- { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
- { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
- { browser: "other", visitors: 90, fill: "var(--color-other)" },
-];
-
-const chartConfig = {
- visitors: {
- label: "Visitors",
- },
- chrome: {
- label: "Chrome",
- color: "var(--chart-1)",
- },
- safari: {
- label: "Safari",
- color: "var(--chart-2)",
- },
- firefox: {
- label: "Firefox",
- color: "var(--chart-3)",
- },
- edge: {
- label: "Edge",
- color: "var(--chart-4)",
- },
- other: {
- label: "Other",
- color: "var(--chart-5)",
- },
-} satisfies ChartConfig;
-
-export function ChartBarMixed() {
- return (
-
-
- Bar Chart - Mixed
- January - June 2024
-
-
-
-
-
- chartConfig[value as keyof typeof chartConfig]?.label
- }
- />
-
- }
- />
-
-
-
-
-
-
- Trending up by 5.2% this month
-
-
- Showing total visitors for the last 6 months
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-demo.tsx b/apps/docs/src/components/docs/primitives/chart-demo.tsx
deleted file mode 100644
index 97dfeec3..00000000
--- a/apps/docs/src/components/docs/primitives/chart-demo.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ChartAreaDemo } from "@/components/docs/primitives/chart-area-demo";
-import { ChartBandedDemo } from "@/components/docs/primitives/chart-banded-demo";
-import { ChartBarDemo } from "@/components/docs/primitives/chart-bar-demo";
-import { ChartBarMixed } from "@/components/docs/primitives/chart-bar-mixed";
-import { ChartLineDemo } from "@/components/docs/primitives/chart-line-demo";
-import { ChartPieDemo } from "@/components/docs/primitives/chart-pie-demo";
-import { ChartPosNegBarDemo } from "@/components/docs/primitives/chart-posneg-bar-demo";
-
-export function ChartDemo() {
- return (
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-line-demo.tsx b/apps/docs/src/components/docs/primitives/chart-line-demo.tsx
deleted file mode 100644
index 8c48a194..00000000
--- a/apps/docs/src/components/docs/primitives/chart-line-demo.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "ghost-ui";
-import { TrendingUp } from "lucide-react";
-import { CartesianGrid, Line, LineChart, XAxis } from "recharts";
-
-export const description = "A multiple line chart";
-
-const chartData = [
- { month: "January", desktop: 186, mobile: 80 },
- { month: "February", desktop: 305, mobile: 200 },
- { month: "March", desktop: 237, mobile: 120 },
- { month: "April", desktop: 73, mobile: 190 },
- { month: "May", desktop: 209, mobile: 130 },
- { month: "June", desktop: 214, mobile: 140 },
-];
-
-const chartConfig = {
- desktop: {
- label: "Desktop",
- color: "var(--chart-1)",
- },
- mobile: {
- label: "Mobile",
- color: "var(--chart-2)",
- },
-} satisfies ChartConfig;
-
-export function ChartLineDemo() {
- return (
-
-
- Line Chart - Multiple
- January - June 2024
-
-
-
-
-
- value.slice(0, 3)}
- />
- } />
-
-
-
-
-
-
-
-
-
- Trending up by 5.2% this month
-
-
- Showing total visitors for the last 6 months
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-pie-demo.tsx b/apps/docs/src/components/docs/primitives/chart-pie-demo.tsx
deleted file mode 100644
index da29b969..00000000
--- a/apps/docs/src/components/docs/primitives/chart-pie-demo.tsx
+++ /dev/null
@@ -1,151 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
-} from "ghost-ui";
-import { TrendingUp } from "lucide-react";
-import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
-
-export const description = "A pie chart with padding angles and hover effects";
-
-const chartData = [
- { name: "Desktop", value: 45, color: "var(--color-desktop)" },
- { name: "Mobile", value: 35, color: "var(--color-mobile)" },
- { name: "Tablet", value: 20, color: "var(--color-tablet)" },
-];
-
-const chartConfig = {
- desktop: {
- label: "Desktop",
- color: "var(--chart-1)",
- },
- mobile: {
- label: "Mobile",
- color: "var(--chart-2)",
- },
- tablet: {
- label: "Tablet",
- color: "var(--chart-3)",
- },
-} satisfies ChartConfig;
-
-const renderCustomizedLabel = ({
- cx,
- cy,
- midAngle,
- outerRadius,
- payload,
- fill,
- value,
-}: any) => {
- const RADIAN = Math.PI / 180;
- const sin = Math.sin(-RADIAN * midAngle);
- const cos = Math.cos(-RADIAN * midAngle);
-
- // Extend line beyond the outer radius
- const mx = cx + (outerRadius + 15) * cos;
- const my = cy + (outerRadius + 15) * sin;
- const ex = mx + (cos >= 0 ? 1 : -1) * 22;
- const ey = my;
-
- // Text anchor based on which side of the pie we're on
- const textAnchor = cos >= 0 ? "start" : "end";
-
- return (
-
- {/* Label line */}
-
- {/* Label text */}
- = 0 ? 10 : -10)}
- y={ey}
- textAnchor={textAnchor}
- fill="var(--foreground)"
- className="text-xs"
- >
- {payload.name} ({value}%)
-
-
- );
-};
-
-const renderActiveShape = (props: any) => {
- const { cx, cy, innerRadius, outerRadius, startAngle, endAngle, fill } =
- props;
-
- return (
-
- );
-};
-
-export function ChartPieDemo() {
- return (
-
-
- Device Distribution
- Traffic sources by device type
-
-
-
-
-
-
- {chartData.map((entry, index) => (
- |
- ))}
-
-
-
-
-
-
-
- Desktop usage up 5.2%
-
-
- Based on total visits in last 6 months
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/chart-posneg-bar-demo.tsx b/apps/docs/src/components/docs/primitives/chart-posneg-bar-demo.tsx
deleted file mode 100644
index 4686107b..00000000
--- a/apps/docs/src/components/docs/primitives/chart-posneg-bar-demo.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-"use client";
-
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- ChartConfig,
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
-} from "ghost-ui";
-import { TrendingUp } from "lucide-react";
-import {
- Bar,
- BarChart,
- CartesianGrid,
- ReferenceLine,
- XAxis,
- YAxis,
-} from "recharts";
-
-export const description = "A bar chart showing positive and negative values";
-
-const chartData = [
- { month: "Jan", value: 8.2 },
- { month: "Feb", value: -4.5 },
- { month: "Mar", value: 6.8 },
- { month: "Apr", value: -3.2 },
- { month: "May", value: 9.4 },
- { month: "Jun", value: -5.1 },
-];
-
-const chartConfig = {
- value: {
- label: "Growth Rate",
- color: "var(--chart-1)",
- },
-} satisfies ChartConfig;
-
-const CustomBar = (props: any) => {
- const { fill, x, y, width, height, value } = props;
- const isNegative = value < 0;
- const barFill = isNegative
- ? "var(--background-danger)"
- : "var(--background-success)";
-
- // For negative values, we want to start from the zero line and go up
- const barHeight = Math.abs(height);
- const barY = isNegative ? y - barHeight : y;
-
- return (
-
-
-
- );
-};
-
-export function ChartPosNegBarDemo() {
- return (
-
-
- Growth Rate
- Monthly growth rate variations
-
-
-
-
-
-
- `${value}%`}
- />
-
- }
- />
- } />
-
-
-
-
-
- Net positive trend
-
-
- Showing monthly growth rate changes
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/checkbox-demo.tsx b/apps/docs/src/components/docs/primitives/checkbox-demo.tsx
deleted file mode 100644
index 489edee1..00000000
--- a/apps/docs/src/components/docs/primitives/checkbox-demo.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-"use client";
-
-import { Checkbox, Label } from "ghost-ui";
-
-export function CheckboxDemo() {
- return (
-
-
-
- Accept terms and conditions
-
-
-
-
-
Accept terms and conditions
-
- By clicking this checkbox, you agree to the terms and conditions.
-
-
-
-
-
- Enable notifications
-
-
-
-
-
Enable notifications
-
- You can enable or disable notifications at any time.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/collapsible-demo.tsx b/apps/docs/src/components/docs/primitives/collapsible-demo.tsx
deleted file mode 100644
index 74a3c3f2..00000000
--- a/apps/docs/src/components/docs/primitives/collapsible-demo.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-"use client";
-
-import {
- Button,
- Collapsible,
- CollapsibleContent,
- CollapsibleTrigger,
-} from "ghost-ui";
-import { ChevronsUpDown } from "lucide-react";
-import * as React from "react";
-
-export function CollapsibleDemo() {
- const [isOpen, setIsOpen] = React.useState(false);
-
- return (
-
-
-
- @peduarte starred 3 repositories
-
-
-
-
- Toggle
-
-
-
-
- @radix-ui/primitives
-
-
-
- @radix-ui/colors
-
-
- @stitches/react
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/combobox-demo.tsx b/apps/docs/src/components/docs/primitives/combobox-demo.tsx
deleted file mode 100644
index bc28ca0f..00000000
--- a/apps/docs/src/components/docs/primitives/combobox-demo.tsx
+++ /dev/null
@@ -1,400 +0,0 @@
-"use client";
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Button,
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
- CommandSeparator,
- cn,
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "ghost-ui";
-import {
- CheckIcon,
- ChevronDownIcon,
- ChevronsUpDown,
- PlusCircleIcon,
-} from "lucide-react";
-import * as React from "react";
-
-const frameworks = [
- {
- value: "next.js",
- label: "Next.js",
- },
- {
- value: "sveltekit",
- label: "SvelteKit",
- },
- {
- value: "nuxt.js",
- label: "Nuxt.js",
- },
- {
- value: "remix",
- label: "Remix",
- },
- {
- value: "astro",
- label: "Astro",
- },
-];
-
-type Framework = (typeof frameworks)[number];
-
-const users = [
- {
- id: "1",
- username: "nahiyankhan",
- },
- {
- id: "2",
- username: "baxen",
- },
- {
- id: "3",
- username: "spencrmartin",
- },
-] as const;
-
-type User = (typeof users)[number];
-
-const timezones = [
- {
- label: "Americas",
- timezones: [
- { value: "America/New_York", label: "(GMT-5) New York" },
- { value: "America/Los_Angeles", label: "(GMT-8) Los Angeles" },
- { value: "America/Chicago", label: "(GMT-6) Chicago" },
- { value: "America/Toronto", label: "(GMT-5) Toronto" },
- { value: "America/Vancouver", label: "(GMT-8) Vancouver" },
- { value: "America/Sao_Paulo", label: "(GMT-3) São Paulo" },
- ],
- },
- {
- label: "Europe",
- timezones: [
- { value: "Europe/London", label: "(GMT+0) London" },
- { value: "Europe/Paris", label: "(GMT+1) Paris" },
- { value: "Europe/Berlin", label: "(GMT+1) Berlin" },
- { value: "Europe/Rome", label: "(GMT+1) Rome" },
- { value: "Europe/Madrid", label: "(GMT+1) Madrid" },
- { value: "Europe/Amsterdam", label: "(GMT+1) Amsterdam" },
- ],
- },
- {
- label: "Asia/Pacific",
- timezones: [
- { value: "Asia/Tokyo", label: "(GMT+9) Tokyo" },
- { value: "Asia/Shanghai", label: "(GMT+8) Shanghai" },
- { value: "Asia/Singapore", label: "(GMT+8) Singapore" },
- { value: "Asia/Dubai", label: "(GMT+4) Dubai" },
- { value: "Australia/Sydney", label: "(GMT+11) Sydney" },
- { value: "Asia/Seoul", label: "(GMT+9) Seoul" },
- ],
- },
-] as const;
-
-type Timezone = (typeof timezones)[number];
-
-export function ComboboxDemo() {
- return (
-
-
-
-
-
-
- );
-}
-
-function FrameworkCombobox({ frameworks }: { frameworks: Framework[] }) {
- const [open, setOpen] = React.useState(false);
- const [value, setValue] = React.useState("");
-
- return (
-
-
-
- {value
- ? frameworks.find((framework) => framework.value === value)?.label
- : "Select framework..."}
-
-
-
-
-
-
-
- No framework found.
-
- {frameworks.map((framework) => (
- {
- setValue(currentValue === value ? "" : currentValue);
- setOpen(false);
- }}
- >
- {framework.label}
-
-
- ))}
-
-
-
-
-
- );
-}
-
-function UserCombobox({
- users,
- selectedUserId,
-}: {
- users: User[];
- selectedUserId: string;
-}) {
- const [open, setOpen] = React.useState(false);
- const [value, setValue] = React.useState(selectedUserId);
-
- const selectedUser = React.useMemo(
- () => users.find((user) => user.id === value),
- [value, users],
- );
-
- return (
-
-
-
- {selectedUser ? (
-
-
-
- {selectedUser.username[0]}
-
- {selectedUser.username}
-
- ) : (
- "Select user..."
- )}
-
-
-
-
-
-
-
- No user found.
-
- {users.map((user) => (
- {
- setValue(currentValue === value ? "" : currentValue);
- setOpen(false);
- }}
- >
-
-
- {user.username[0]}
-
- {user.username}
-
-
- ))}
-
-
-
-
-
- Create user
-
-
-
-
-
-
- );
-}
-
-function TimezoneCombobox({
- timezones,
- selectedTimezone,
-}: {
- timezones: Timezone[];
- selectedTimezone: Timezone["timezones"][number];
-}) {
- const [open, setOpen] = React.useState(false);
- const [value, setValue] = React.useState(selectedTimezone.value);
-
- const selectedGroup = React.useMemo(
- () =>
- timezones.find((group) =>
- group.timezones.find((tz) => tz.value === value),
- ),
- [value, timezones],
- );
-
- const selectedTimezoneLabel = React.useMemo(
- () => selectedGroup?.timezones.find((tz) => tz.value === value)?.label,
- [value, selectedGroup],
- );
-
- return (
-
-
-
- {selectedTimezone ? (
-
-
- {selectedGroup?.label}
-
- {selectedTimezoneLabel}
-
- ) : (
- "Select timezone"
- )}
-
-
-
-
-
-
-
- No timezone found.
- {timezones.map((region) => (
-
- {region.timezones.map((timezone) => (
- {
- setValue(
- currentValue as Timezone["timezones"][number]["value"],
- );
- setOpen(false);
- }}
- >
- {timezone.label}
-
-
- ))}
-
- ))}
-
-
-
-
- Create timezone
-
-
-
-
-
-
- );
-}
-
-function ComboboxWithCheckbox({ frameworks }: { frameworks: Framework[] }) {
- const [open, setOpen] = React.useState(false);
- const [selectedFrameworks, setSelectedFrameworks] = React.useState<
- Framework[]
- >([]);
-
- return (
-
-
-
- {selectedFrameworks.length > 0
- ? selectedFrameworks.map((framework) => framework.label).join(", ")
- : "Select frameworks (multi-select)..."}
-
-
-
-
-
-
-
- No framework found.
-
- {frameworks.map((framework) => (
- {
- setSelectedFrameworks(
- selectedFrameworks.some((f) => f.value === currentValue)
- ? selectedFrameworks.filter(
- (f) => f.value !== currentValue,
- )
- : [...selectedFrameworks, framework],
- );
- }}
- >
- f.value === framework.value,
- )}
- >
-
-
- {framework.label}
-
- ))}
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/command-demo.tsx b/apps/docs/src/components/docs/primitives/command-demo.tsx
deleted file mode 100644
index 34021ae0..00000000
--- a/apps/docs/src/components/docs/primitives/command-demo.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-"use client";
-
-import {
- CommandDialog,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
- CommandSeparator,
- CommandShortcut,
-} from "ghost-ui";
-import {
- Calculator,
- Calendar,
- CreditCard,
- Settings,
- Smile,
- User,
-} from "lucide-react";
-import * as React from "react";
-
-export function CommandDemo() {
- const [open, setOpen] = React.useState(false);
-
- React.useEffect(() => {
- const down = (e: KeyboardEvent) => {
- if (e.key === "j" && (e.metaKey || e.ctrlKey)) {
- e.preventDefault();
- setOpen((open) => !open);
- }
- };
-
- document.addEventListener("keydown", down);
- return () => document.removeEventListener("keydown", down);
- }, []);
-
- return (
- <>
-
- Press{" "}
-
- ⌘ J
-
-
-
-
-
- No results found.
-
-
-
- Calendar
-
-
-
- Search Emoji
-
-
-
- Calculator
-
-
-
-
-
-
- Profile
- ⌘P
-
-
-
- Billing
- ⌘B
-
-
-
- Settings
- ⌘S
-
-
-
-
- >
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/component-wrapper.tsx b/apps/docs/src/components/docs/primitives/component-wrapper.tsx
deleted file mode 100644
index f6193379..00000000
--- a/apps/docs/src/components/docs/primitives/component-wrapper.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-// pulled from https://github.com/shadcn-ui/ui/blob/main/apps/v4/components/component-wrapper.tsx#L7
-"use client";
-
-import {
- Alert,
- AlertTitle,
- Button,
- cn,
- getComponentName,
- useCopyToClipboard,
-} from "ghost-ui";
-import { Check, Clipboard, Expand, Globe, Hash, Terminal } from "lucide-react";
-import * as React from "react";
-import { ComponentErrorBoundary } from "@/components/docs/error-boundary";
-import { ExternalLink } from "@/components/docs/external-link";
-import { TooltipWrapper } from "@/components/docs/tooltip-wrapper";
-
-export function ComponentWrapper({
- className,
- name,
- children,
- internalUrl,
- showUrl = false,
- ...props
-}: React.ComponentPropsWithoutRef<"div"> & {
- name: string;
- internalUrl?: string;
- showUrl?: boolean;
-}) {
- const { copyToClipboard, isCopied } = useCopyToClipboard();
- const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
- const [isHashCopied, setIsHashCopied] = React.useState(false);
-
- const handleHashCopy = () => {
- const url = `${window.location.origin}${
- window.location.pathname
- }#${name.toLowerCase()}`;
- copyToClipboard(url);
- setIsHashCopied(true);
- setTimeout(() => setIsHashCopied(false), 2000);
- };
-
- React.useEffect(() => {
- // Enable smooth scrolling for anchor links
- document.documentElement.style.scrollBehavior = "smooth";
- return () => {
- document.documentElement.style.scrollBehavior = "";
- };
- }, []);
-
- return (
-
-
-
-
-
-
-
-
- {getComponentName(name)}
-
-
-
-
-
-
-
-
-
- {/*
-
-
-
-
-
- {`npx shadcn@latest add `}
- {name}
-
-
-
-
-
- copyToClipboard(`npx shadcn@latest add ${name}`)
- }
- >
-
-
-
-
-
- {showUrl && (
-
-
- {internalUrl ? (
-
- ) : (
-
- )}
-
-
- )}
-
- */}
-
-
-
- {children}
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/context-menu-demo.tsx b/apps/docs/src/components/docs/primitives/context-menu-demo.tsx
deleted file mode 100644
index f97a1a9f..00000000
--- a/apps/docs/src/components/docs/primitives/context-menu-demo.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import {
- ContextMenu,
- ContextMenuCheckboxItem,
- ContextMenuContent,
- ContextMenuItem,
- ContextMenuLabel,
- ContextMenuRadioGroup,
- ContextMenuRadioItem,
- ContextMenuSeparator,
- ContextMenuShortcut,
- ContextMenuSub,
- ContextMenuSubContent,
- ContextMenuSubTrigger,
- ContextMenuTrigger,
-} from "ghost-ui";
-import { Code2Icon, PlusIcon, TrashIcon } from "lucide-react";
-
-export function ContextMenuDemo() {
- return (
-
-
- Right click here
-
-
-
- Back
- ⌘[
-
-
- Forward
- ⌘]
-
-
- Reload
- ⌘R
-
-
- More Tools
-
-
- Save Page...
- ⇧⌘S
-
-
-
- Create Shortcut...
-
- Name Window...
-
-
-
- Developer Tools
-
-
-
-
- Delete
-
-
-
-
-
- Show Bookmarks Bar
- ⌘⇧B
-
- Show Full URLs
-
-
- People
-
- Pedro Duarte
-
- Colm Tuite
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/date-picker-demo.tsx b/apps/docs/src/components/docs/primitives/date-picker-demo.tsx
deleted file mode 100644
index 13066b65..00000000
--- a/apps/docs/src/components/docs/primitives/date-picker-demo.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-"use client";
-
-import { addDays, format } from "date-fns";
-import {
- Button,
- Calendar,
- cn,
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "ghost-ui";
-import { CalendarIcon } from "lucide-react";
-import * as React from "react";
-import { DateRange } from "react-day-picker";
-
-export function DatePickerDemo() {
- return (
-
-
-
-
- );
-}
-
-function DatePickerSimple() {
- const [date, setDate] = React.useState();
-
- return (
-
-
-
-
- {date ? format(date, "PPP") : Pick a date }
-
-
-
-
-
-
- );
-}
-
-function DatePickerWithRange() {
- const [date, setDate] = React.useState({
- from: new Date(new Date().getFullYear(), 0, 20),
- to: addDays(new Date(new Date().getFullYear(), 0, 20), 20),
- });
-
- return (
-
-
-
-
- {date?.from ? (
- date.to ? (
- <>
- {format(date.from, "LLL dd, y")} -{" "}
- {format(date.to, "LLL dd, y")}
- >
- ) : (
- format(date.from, "LLL dd, y")
- )
- ) : (
- Pick a date
- )}
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/dialog-demo.tsx b/apps/docs/src/components/docs/primitives/dialog-demo.tsx
deleted file mode 100644
index d3bc7a4b..00000000
--- a/apps/docs/src/components/docs/primitives/dialog-demo.tsx
+++ /dev/null
@@ -1,133 +0,0 @@
-import {
- Button,
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
- Input,
- Label,
-} from "ghost-ui";
-
-export function DialogDemo() {
- return (
-
-
-
-
-
- );
-}
-
-function DialogWithForm() {
- return (
-
-
-
- );
-}
-
-function DialogScrollableContent() {
- return (
-
-
- Scrollable Content
-
-
-
- Scrollable Content
-
- This is a dialog with scrollable content.
-
-
-
-
- Lorem Ipsum
-
- {Array.from({ length: 10 }).map((_, index) => (
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
- enim ad minim veniam, quis nostrud exercitation ullamco laboris
- nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
- reprehenderit in voluptate velit esse cillum dolore eu fugiat
- nulla pariatur. Excepteur sint occaecat cupidatat non proident,
- sunt in culpa qui officia deserunt mollit anim id est laborum.
-
- ))}
-
-
-
- );
-}
-
-function DialogWithStickyFooter() {
- return (
-
-
- Sticky Footer
-
-
-
- Scrollable Content
-
- This is a dialog with scrollable content.
-
-
-
-
- Lorem Ipsum
-
- {Array.from({ length: 10 }).map((_, index) => (
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
- enim ad minim veniam, quis nostrud exercitation ullamco laboris
- nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
- reprehenderit in voluptate velit esse cillum dolore eu fugiat
- nulla pariatur. Excepteur sint occaecat cupidatat non proident,
- sunt in culpa qui officia deserunt mollit anim id est laborum.
-
- ))}
-
-
-
- Close
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/drawer-demo.tsx b/apps/docs/src/components/docs/primitives/drawer-demo.tsx
deleted file mode 100644
index 530deefd..00000000
--- a/apps/docs/src/components/docs/primitives/drawer-demo.tsx
+++ /dev/null
@@ -1,212 +0,0 @@
-"use client";
-
-import {
- Button,
- Drawer,
- DrawerClose,
- DrawerContent,
- DrawerDescription,
- DrawerFooter,
- DrawerHeader,
- DrawerTitle,
- DrawerTrigger,
-} from "ghost-ui";
-import { Minus, Plus } from "lucide-react";
-import * as React from "react";
-import { Bar, BarChart, ResponsiveContainer } from "recharts";
-
-const data = [
- {
- goal: 400,
- },
- {
- goal: 300,
- },
- {
- goal: 200,
- },
- {
- goal: 300,
- },
- {
- goal: 200,
- },
- {
- goal: 278,
- },
- {
- goal: 189,
- },
- {
- goal: 239,
- },
- {
- goal: 300,
- },
- {
- goal: 200,
- },
- {
- goal: 278,
- },
- {
- goal: 189,
- },
- {
- goal: 349,
- },
-];
-
-export function DrawerDemo() {
- return (
-
-
-
-
-
- );
-}
-
-function DrawerBottom() {
- const [goal, setGoal] = React.useState(350);
-
- const onClick = React.useCallback((adjustment: number) => {
- setGoal((prevGoal) => Math.max(200, Math.min(400, prevGoal + adjustment)));
- }, []);
-
- return (
-
-
- Open Drawer
-
-
-
-
- Payment Amount
- Set your payment amount.
-
-
-
-
onClick(-10)}
- disabled={goal <= 200}
- >
-
- Decrease
-
-
-
onClick(10)}
- disabled={goal >= 400}
- >
-
- Increase
-
-
-
-
- Pay
-
- Cancel
-
-
-
-
-
- );
-}
-
-function DrawerScrollableContent() {
- return (
-
-
- Scrollable Content
-
-
-
- Payment Amount
- Set your payment amount.
-
-
-
- Lorem Ipsum
-
- {Array.from({ length: 10 }).map((_, index) => (
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
- enim ad minim veniam, quis nostrud exercitation ullamco laboris
- nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
- reprehenderit in voluptate velit esse cillum dolore eu fugiat
- nulla pariatur. Excepteur sint occaecat cupidatat non proident,
- sunt in culpa qui officia deserunt mollit anim id est laborum.
-
- ))}
-
-
- Submit
-
- Cancel
-
-
-
-
- );
-}
-
-const directions = ["top", "right", "bottom", "left"] as const;
-
-function DrawerDirections() {
- return (
- <>
- {directions.map((direction) => (
-
-
-
- {direction}
-
-
-
-
- Move Goal
-
- Set your daily activity goal.
-
-
-
- {Array.from({ length: 10 }).map((_, index) => (
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
- do eiusmod tempor incididunt ut labore et dolore magna aliqua.
- Ut enim ad minim veniam, quis nostrud exercitation ullamco
- laboris nisi ut aliquip ex ea commodo consequat. Duis aute
- irure dolor in reprehenderit in voluptate velit esse cillum
- dolore eu fugiat nulla pariatur. Excepteur sint occaecat
- cupidatat non proident, sunt in culpa qui officia deserunt
- mollit anim id est laborum.
-
- ))}
-
-
- Submit
-
- Cancel
-
-
-
-
- ))}
- >
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/dropdown-menu-demo.tsx b/apps/docs/src/components/docs/primitives/dropdown-menu-demo.tsx
deleted file mode 100644
index 97b56b4f..00000000
--- a/apps/docs/src/components/docs/primitives/dropdown-menu-demo.tsx
+++ /dev/null
@@ -1,369 +0,0 @@
-"use client";
-
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Button,
- DropdownMenu,
- DropdownMenuCheckboxItem,
- DropdownMenuContent,
- DropdownMenuGroup,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuPortal,
- DropdownMenuRadioGroup,
- DropdownMenuRadioItem,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuTrigger,
-} from "ghost-ui";
-import {
- BadgeCheckIcon,
- BellIcon,
- ChevronsUpDownIcon,
- CreditCardIcon,
- LogOut,
- LogOutIcon,
- MoreHorizontalIcon,
- PencilIcon,
- Settings2Icon,
- ShareIcon,
- SparklesIcon,
- TrashIcon,
- UserIcon,
-} from "lucide-react";
-import * as React from "react";
-
-export function DropdownMenuDemo() {
- return (
-
-
-
-
-
-
-
-
- );
-}
-
-function DropdownMenuSimple() {
- return (
-
-
- Open
-
-
- My Account
-
-
- Profile
- ⇧⌘P
-
-
- Billing
- ⌘B
-
-
- Settings
- ⌘S
-
-
- Keyboard shortcuts
- ⌘K
-
-
-
-
- Team
-
- Invite users
-
-
- Email
- Message
-
- More...
-
-
-
-
- New Team
- ⌘+T
-
-
-
- GitHub
- Support
- API
-
-
- Log out
- ⇧⌘Q
-
-
-
- );
-}
-
-function DropdownMenuCheckboxes() {
- const [showStatusBar, setShowStatusBar] = React.useState(true);
- const [showActivityBar, setShowActivityBar] = React.useState(false);
- const [showPanel, setShowPanel] = React.useState(false);
-
- return (
-
-
- Checkboxes
-
-
-
- Account
-
- Profile
-
-
- Billing
-
-
- Settings
-
-
-
-
- Appearance
-
- Status Bar
-
-
- Activity Bar
-
-
- Panel
-
-
-
-
-
- Sign Out
-
-
-
-
- );
-}
-
-function DropdownMenuRadioGroupDemo() {
- const [position, setPosition] = React.useState("bottom");
-
- return (
-
-
- Radio Group
-
-
- Panel Position
-
-
- Top
- Bottom
-
- Right
-
-
-
-
-
- );
-}
-
-function DropdownMenuWithAvatar() {
- return (
-
-
-
-
-
- NK
-
-
- nahiyankhan
-
- nahiyankhan@example.com
-
-
-
-
-
-
-
-
-
-
- NK
-
-
- nahiyankhan
-
- nahiyankhan@example.com
-
-
-
-
-
-
-
-
- Upgrade to Pro
-
-
-
-
-
-
- Account
-
-
-
- Billing
-
-
-
- Notifications
-
-
-
-
-
- Sign Out
-
-
-
- );
-}
-
-function DropdownMenuAvatarOnly() {
- return (
-
-
-
-
-
- NK
-
-
-
-
-
-
-
-
- NK
-
-
- nahiyankhan
-
- nahiyankhan@example.com
-
-
-
-
-
-
-
-
- Upgrade to Pro
-
-
-
-
-
-
- Account
-
-
-
- Billing
-
-
-
- Notifications
-
-
-
-
-
- Sign Out
-
-
-
- );
-}
-
-function DropdownMenuIconColor() {
- return (
-
-
-
-
- Toggle menu
-
-
-
-
-
-
- Edit
-
-
-
- Share
-
-
-
-
- Delete
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/form-demo.tsx b/apps/docs/src/components/docs/primitives/form-demo.tsx
deleted file mode 100644
index 56b5895c..00000000
--- a/apps/docs/src/components/docs/primitives/form-demo.tsx
+++ /dev/null
@@ -1,419 +0,0 @@
-"use client";
-
-import { zodResolver } from "@hookform/resolvers/zod";
-import { format } from "date-fns";
-import {
- Button,
- Calendar,
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
- Checkbox,
- cn,
- Form,
- FormControl,
- FormDescription,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
- Input,
- Popover,
- PopoverContent,
- PopoverTrigger,
- RadioGroup,
- RadioGroupItem,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- Switch,
- Textarea,
-} from "ghost-ui";
-import { CalendarIcon } from "lucide-react";
-import { useForm } from "react-hook-form";
-import { toast } from "sonner";
-import { z } from "zod";
-
-const items = [
- {
- id: "recents",
- label: "Recents",
- },
- {
- id: "home",
- label: "Home",
- },
- {
- id: "applications",
- label: "Applications",
- },
- {
- id: "desktop",
- label: "Desktop",
- },
- {
- id: "downloads",
- label: "Downloads",
- },
- {
- id: "documents",
- label: "Documents",
- },
-] as const;
-
-const FormSchema = z.object({
- username: z.string().min(2, {
- message: "Username must be at least 2 characters.",
- }),
- bio: z
- .string()
- .min(10, {
- message: "Bio must be at least 10 characters.",
- })
- .max(160, {
- message: "Bio must not be longer than 30 characters.",
- }),
- email: z
- .string({
- error: "Please select an email to display.",
- })
- .email(),
- type: z.enum(["all", "mentions", "none"], {
- error: "You need to select a notification type.",
- }),
- mobile: z.boolean().default(false).optional(),
- items: z.array(z.string()).refine((value) => value.some((item) => item), {
- message: "You have to select at least one item.",
- }),
- dob: z.date({
- error: "A date of birth is required.",
- }),
- marketing_emails: z.boolean().default(false).optional(),
- security_emails: z.boolean(),
-});
-
-export function FormDemo() {
- const form = useForm>({
- resolver: zodResolver(FormSchema),
- defaultValues: {
- username: "",
- items: ["recents", "home"],
- },
- });
-
- function onSubmit(data: z.infer) {
- toast("You submitted the following values:", {
- description: (
-
- {JSON.stringify(data, null, 2)}
-
- ),
- });
- }
-
- return (
-
-
- Example
-
- See some of the form building blocks.
-
-
-
-
- {" "}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/forms-demo.tsx b/apps/docs/src/components/docs/primitives/forms-demo.tsx
deleted file mode 100644
index 723dbb32..00000000
--- a/apps/docs/src/components/docs/primitives/forms-demo.tsx
+++ /dev/null
@@ -1,227 +0,0 @@
-"use client";
-
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- Checkbox,
- Input,
- Label,
- RadioGroup,
- RadioGroupItem,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- Textarea,
- useTheme,
-} from "ghost-ui";
-import * as React from "react";
-
-const plans = [
- {
- id: "starter",
- name: "Starter Plan",
- description: "Perfect for small businesses.",
- price: "$10",
- },
- {
- id: "pro",
- name: "Pro Plan",
- description: "Advanced features with more storage.",
- price: "$20",
- },
-] as const;
-
-const themes = {
- neutral: {
- light: {
- "--primary": "oklch(0.205 0 0)",
- "--primary-foreground": "oklch(0.985 0 0)",
- "--ring": "oklch(0.708 0 0)",
- },
- dark: {
- "--primary": "oklch(0.922 0 0)",
- "--primary-foreground": "oklch(0.205 0 0)",
- "--ring": "oklch(0.556 0 0)",
- },
- },
- blue: {
- light: {
- "--primary": "oklch(0.546 0.245 262.881)",
- "--primary-foreground": "oklch(0.985 0.001 106.423)",
- "--ring": "oklch(0.546 0.245 262.881)",
- },
- dark: {
- "--primary": "oklch(0.623 0.214 259.815)",
- "--primary-foreground": "oklch(0.985 0.001 106.423)",
- "--ring": "oklch(0.623 0.214 259.815)",
- },
- },
- amber: {
- light: {
- "--primary": "oklch(0.769 0.188 70.08)",
- "--primary-foreground": "oklch(0.985 0.001 106.423)",
- "--ring": "oklch(0.82 0.13 92.25)",
- },
- dark: {
- "--primary": "oklch(0.769 0.188 70.08)",
- "--primary-foreground": "oklch(0.216 0.006 56.043)",
- "--ring": "oklch(0.666 0.179 58.318)",
- },
- },
- teal: {
- light: {
- "--primary": "oklch(0.627 0.194 149.214)",
- "--primary-foreground": "oklch(0.985 0.001 106.423)",
- "--ring": "oklch(0.79 0.19 153.13)",
- },
- dark: {
- "--primary": "oklch(0.704 0.14 182.503)",
- "--primary-foreground": "oklch(0.216 0.006 56.043)",
- "--ring": "oklch(0.704 0.14 182.503)",
- },
- },
-} as const;
-
-export function FormsDemo() {
- const { theme: mode = "light" } = useTheme();
- const [theme, setTheme] = React.useState(
- undefined,
- );
-
- const themeStyles = React.useMemo(() => {
- if (!theme) return undefined;
- return themes[theme][mode as keyof (typeof themes)[typeof theme]];
- }, [theme, mode]);
-
- return (
-
-
-
- Upgrade your subscription
-
- You are currently on the free plan. Upgrade to the pro plan to get
- access to all features.
-
-
-
-
-
-
-
-
Color
-
- setTheme(value as keyof typeof themes)
- }
- >
-
-
-
-
- {Object.keys(themes).map((theme) => (
-
-
- {theme}
-
- ))}
-
-
-
-
- Plan
-
- Select the plan that best fits your needs.
-
-
- {plans.map((plan) => (
-
-
-
-
{plan.name}
-
- {plan.description}
-
-
-
- ))}
-
-
-
- Notes
-
-
-
-
-
-
- I agree to the terms and conditions
-
-
-
-
-
- Allow us to send you emails
-
-
-
-
-
-
-
- Cancel
-
- Upgrade Plan
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/hover-card-demo.tsx b/apps/docs/src/components/docs/primitives/hover-card-demo.tsx
deleted file mode 100644
index d2104e43..00000000
--- a/apps/docs/src/components/docs/primitives/hover-card-demo.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Button,
- HoverCard,
- HoverCardContent,
- HoverCardTrigger,
-} from "ghost-ui";
-import { CalendarIcon } from "lucide-react";
-
-export function HoverCardDemo() {
- return (
-
-
- @nextjs
-
-
-
-
-
- VC
-
-
-
@nextjs
-
- The React Framework – created and maintained by @vercel.
-
-
- {" "}
-
- Joined December 2021
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/index.tsx b/apps/docs/src/components/docs/primitives/index.tsx
deleted file mode 100644
index 5b0a85b1..00000000
--- a/apps/docs/src/components/docs/primitives/index.tsx
+++ /dev/null
@@ -1,200 +0,0 @@
-"use client";
-
-import { AccordionDemo } from "@/components/docs/primitives/accordion-demo";
-import { AlertDemo } from "@/components/docs/primitives/alert-demo";
-import { AlertDialogDemo } from "@/components/docs/primitives/alert-dialog-demo";
-import { AspectRatioDemo } from "@/components/docs/primitives/aspect-ratio-demo";
-import { AvatarDemo } from "@/components/docs/primitives/avatar-demo";
-import { BadgeDemo } from "@/components/docs/primitives/badge-demo";
-import { BreadcrumbDemo } from "@/components/docs/primitives/breadcrumb-demo";
-import { ButtonDemo } from "@/components/docs/primitives/button-demo";
-import { CalendarDemo } from "@/components/docs/primitives/calendar-demo";
-import { CardDemo } from "@/components/docs/primitives/card-demo";
-import { CarouselDemo } from "@/components/docs/primitives/carousel-demo";
-import { ChartDemo } from "@/components/docs/primitives/chart-demo";
-import { CheckboxDemo } from "@/components/docs/primitives/checkbox-demo";
-import { CollapsibleDemo } from "@/components/docs/primitives/collapsible-demo";
-import { ComboboxDemo } from "@/components/docs/primitives/combobox-demo";
-import { CommandDemo } from "@/components/docs/primitives/command-demo";
-import { ComponentWrapper } from "@/components/docs/primitives/component-wrapper";
-import { ContextMenuDemo } from "@/components/docs/primitives/context-menu-demo";
-import { DatePickerDemo } from "@/components/docs/primitives/date-picker-demo";
-import { DialogDemo } from "@/components/docs/primitives/dialog-demo";
-import { DrawerDemo } from "@/components/docs/primitives/drawer-demo";
-import { DropdownMenuDemo } from "@/components/docs/primitives/dropdown-menu-demo";
-import { FormDemo } from "@/components/docs/primitives/form-demo";
-import { FormsDemo } from "@/components/docs/primitives/forms-demo";
-import { HoverCardDemo } from "@/components/docs/primitives/hover-card-demo";
-import { InputDemo } from "@/components/docs/primitives/input-demo";
-import { InputOTPDemo } from "@/components/docs/primitives/input-otp-demo";
-import { LabelDemo } from "@/components/docs/primitives/label-demo";
-import { MenubarDemo } from "@/components/docs/primitives/menubar-demo";
-import { NavigationMenuDemo } from "@/components/docs/primitives/navigation-menu-demo";
-import { PaginationDemo } from "@/components/docs/primitives/pagination-demo";
-import { PopoverDemo } from "@/components/docs/primitives/popover-demo";
-import { ProgressDemo } from "@/components/docs/primitives/progress-demo";
-import { RadioGroupDemo } from "@/components/docs/primitives/radio-group-demo";
-import { ResizableDemo } from "@/components/docs/primitives/resizable-demo";
-import { ScrollAreaDemo } from "@/components/docs/primitives/scroll-area-demo";
-import { SelectDemo } from "@/components/docs/primitives/select-demo";
-import { SeparatorDemo } from "@/components/docs/primitives/separator-demo";
-import { SheetDemo } from "@/components/docs/primitives/sheet-demo";
-import { SkeletonDemo } from "@/components/docs/primitives/skeleton-demo";
-import { SliderDemo } from "@/components/docs/primitives/slider-demo";
-import { SonnerDemo } from "@/components/docs/primitives/sonner-demo";
-import { SwitchDemo } from "@/components/docs/primitives/switch-demo";
-import { TableDemo } from "@/components/docs/primitives/table-demo";
-import { TabsDemo } from "@/components/docs/primitives/tabs-demo";
-import { TextareaDemo } from "@/components/docs/primitives/textarea-demo";
-import { ToggleDemo } from "@/components/docs/primitives/toggle-demo";
-import { ToggleGroupDemo } from "@/components/docs/primitives/toggle-group-demo";
-import { TooltipDemo } from "@/components/docs/primitives/tooltip-demo";
-
-export function ComponentDemos() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/input-demo.tsx b/apps/docs/src/components/docs/primitives/input-demo.tsx
deleted file mode 100644
index 381a35f2..00000000
--- a/apps/docs/src/components/docs/primitives/input-demo.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Input } from "ghost-ui";
-
-export function InputDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/input-otp-demo.tsx b/apps/docs/src/components/docs/primitives/input-otp-demo.tsx
deleted file mode 100644
index e1aa37d9..00000000
--- a/apps/docs/src/components/docs/primitives/input-otp-demo.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-"use client";
-
-import {
- InputOTP,
- InputOTPGroup,
- InputOTPSeparator,
- InputOTPSlot,
- Label,
-} from "ghost-ui";
-import { REGEXP_ONLY_DIGITS } from "input-otp";
-import * as React from "react";
-
-export function InputOTPDemo() {
- return (
-
-
-
-
-
-
- );
-}
-
-function InputOTPSimple() {
- return (
-
- Simple
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-function InputOTPPattern() {
- return (
-
- Digits Only
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-function InputOTPWithSeparator() {
- const [value, setValue] = React.useState("123456");
-
- return (
-
- With Separator
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-function InputOTPWithSpacing() {
- return (
-
- With Spacing
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/label-demo.tsx b/apps/docs/src/components/docs/primitives/label-demo.tsx
deleted file mode 100644
index 28a83581..00000000
--- a/apps/docs/src/components/docs/primitives/label-demo.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Checkbox, Input, Label, Textarea } from "ghost-ui";
-
-export function LabelDemo() {
- return (
-
-
-
- Accept terms and conditions
-
-
- Username
-
-
-
- Disabled
-
-
-
- Message
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/menubar-demo.tsx b/apps/docs/src/components/docs/primitives/menubar-demo.tsx
deleted file mode 100644
index 78f67072..00000000
--- a/apps/docs/src/components/docs/primitives/menubar-demo.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import {
- Menubar,
- MenubarCheckboxItem,
- MenubarContent,
- MenubarGroup,
- MenubarItem,
- MenubarMenu,
- MenubarRadioGroup,
- MenubarRadioItem,
- MenubarSeparator,
- MenubarShortcut,
- MenubarSub,
- MenubarSubContent,
- MenubarSubTrigger,
- MenubarTrigger,
-} from "ghost-ui";
-import { HelpCircleIcon, SettingsIcon, Trash2Icon } from "lucide-react";
-
-export function MenubarDemo() {
- return (
-
-
- File
-
-
- New Tab ⌘T
-
-
- New Window ⌘N
-
- New Incognito Window
-
-
- Share
-
- Email link
- Messages
- Notes
-
-
-
-
- Print... ⌘P
-
-
-
-
- Edit
-
-
- Undo ⌘Z
-
-
- Redo ⇧⌘Z
-
-
-
- Find
-
- Search the web
-
- Find...
- Find Next
- Find Previous
-
-
-
- Cut
- Copy
- Paste
-
-
-
- View
-
- Always Show Bookmarks Bar
-
- Always Show Full URLs
-
-
-
- Reload ⌘R
-
-
- Force Reload ⇧⌘R
-
-
- Toggle Fullscreen
-
- Hide Sidebar
-
-
-
- Profiles
-
-
- Andy
- Benoit
- Luis
-
-
- Edit...
-
- Add Profile...
-
-
-
- More
-
-
-
-
- Settings
-
-
-
- Help
-
-
-
-
- Delete
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/navigation-menu-demo.tsx b/apps/docs/src/components/docs/primitives/navigation-menu-demo.tsx
deleted file mode 100644
index 08684696..00000000
--- a/apps/docs/src/components/docs/primitives/navigation-menu-demo.tsx
+++ /dev/null
@@ -1,224 +0,0 @@
-import {
- NavigationMenu,
- NavigationMenuContent,
- NavigationMenuItem,
- NavigationMenuLink,
- NavigationMenuList,
- NavigationMenuTrigger,
- navigationMenuTriggerStyle,
-} from "ghost-ui";
-import { CircleCheckIcon, CircleHelpIcon, CircleIcon } from "lucide-react";
-import * as React from "react";
-import { Link } from "react-router";
-
-const components: { title: string; href: string; description: string }[] = [
- {
- title: "Alert Dialog",
- href: "/docs/primitives/alert-dialog",
- description:
- "A modal dialog that interrupts the user with important content and expects a response.",
- },
- {
- title: "Hover Card",
- href: "/docs/primitives/hover-card",
- description:
- "For sighted users to preview content available behind a link.",
- },
- {
- title: "Progress",
- href: "/docs/primitives/progress",
- description:
- "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
- },
- {
- title: "Scroll-area",
- href: "/docs/primitives/scroll-area",
- description: "Visually or semantically separates content.",
- },
- {
- title: "Tabs",
- href: "/docs/primitives/tabs",
- description:
- "A set of layered sections of content—known as tab panels—that are displayed one at a time.",
- },
- {
- title: "Tooltip",
- href: "/docs/primitives/tooltip",
- description:
- "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
- },
-];
-
-export function NavigationMenuDemo() {
- return (
-
-
-
-
- Getting started
-
-
-
-
-
- Components
-
-
- {components.map((component) => (
-
- {component.description}
-
- ))}
-
-
-
-
-
- Documentation
-
-
-
-
-
-
-
-
- Documentation
-
-
-
- List
-
-
-
-
-
- Components
-
- Browse all components in the library.
-
-
-
-
-
- Documentation
-
- Learn how to use the library.
-
-
-
-
-
- Blog
-
- Read our latest blog posts.
-
-
-
-
-
-
-
-
- Simple List
-
-
-
-
- Components
-
-
- Documentation
-
-
- Blocks
-
-
-
-
-
-
- With Icon
-
-
-
-
-
-
- Backlog
-
-
-
-
-
- To Do
-
-
-
-
-
- Done
-
-
-
-
-
-
-
-
-
- );
-}
-
-function ListItem({
- title,
- children,
- href,
- ...props
-}: React.ComponentPropsWithoutRef<"li"> & { href: string }) {
- return (
-
-
-
- {title}
-
- {children}
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/pagination-demo.tsx b/apps/docs/src/components/docs/primitives/pagination-demo.tsx
deleted file mode 100644
index f76b352e..00000000
--- a/apps/docs/src/components/docs/primitives/pagination-demo.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import {
- Pagination,
- PaginationContent,
- PaginationEllipsis,
- PaginationItem,
- PaginationLink,
- PaginationNext,
- PaginationPrevious,
-} from "ghost-ui";
-
-export function PaginationDemo() {
- return (
-
-
-
-
-
-
-
- 1
-
-
-
- 2
-
-
-
- 3
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/popover-demo.tsx b/apps/docs/src/components/docs/primitives/popover-demo.tsx
deleted file mode 100644
index fc32da75..00000000
--- a/apps/docs/src/components/docs/primitives/popover-demo.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import {
- Button,
- Input,
- Label,
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "ghost-ui";
-
-export function PopoverDemo() {
- return (
-
-
- Open popover
-
-
-
-
-
- Dimensions
-
-
- Set the dimensions for the layer.
-
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/progress-demo.tsx b/apps/docs/src/components/docs/primitives/progress-demo.tsx
deleted file mode 100644
index 7d9404e6..00000000
--- a/apps/docs/src/components/docs/primitives/progress-demo.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-"use client";
-
-import { Progress } from "ghost-ui";
-import * as React from "react";
-
-export function ProgressDemo() {
- const [progress, setProgress] = React.useState(13);
-
- React.useEffect(() => {
- const timer = setTimeout(() => setProgress(66), 500);
- return () => clearTimeout(timer);
- }, []);
-
- return ;
-}
diff --git a/apps/docs/src/components/docs/primitives/radio-group-demo.tsx b/apps/docs/src/components/docs/primitives/radio-group-demo.tsx
deleted file mode 100644
index e9acb8e7..00000000
--- a/apps/docs/src/components/docs/primitives/radio-group-demo.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Label, RadioGroup, RadioGroupItem } from "ghost-ui";
-
-const plans = [
- {
- id: "starter",
- name: "Starter Plan",
- description:
- "Perfect for small businesses getting started with our platform",
- price: "$10",
- },
- {
- id: "pro",
- name: "Pro Plan",
- description: "Advanced features for growing businesses with higher demands",
- price: "$20",
- },
-] as const;
-
-export function RadioGroupDemo() {
- return (
-
-
-
-
- Default
-
-
-
- Comfortable
-
-
-
- Compact
-
-
-
- {plans.map((plan) => (
-
-
-
-
{plan.name}
-
- {plan.description}
-
-
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/resizable-demo.tsx b/apps/docs/src/components/docs/primitives/resizable-demo.tsx
deleted file mode 100644
index 835194d5..00000000
--- a/apps/docs/src/components/docs/primitives/resizable-demo.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "ghost-ui";
-
-export function ResizableDemo() {
- return (
-
-
-
-
- One
-
-
-
-
-
-
-
- Two
-
-
-
-
-
- Three
-
-
-
-
-
-
-
-
- Sidebar
-
-
-
-
-
- Content
-
-
-
-
-
-
- Header
-
-
-
-
-
- Content
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/scroll-area-demo.tsx b/apps/docs/src/components/docs/primitives/scroll-area-demo.tsx
deleted file mode 100644
index f21ec8a7..00000000
--- a/apps/docs/src/components/docs/primitives/scroll-area-demo.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { ScrollArea, ScrollBar, Separator } from "ghost-ui";
-import * as React from "react";
-export function ScrollAreaDemo() {
- return (
-
-
-
-
- );
-}
-
-const tags = Array.from({ length: 50 }).map(
- (_, i, a) => `v1.2.0-beta.${a.length - i}`,
-);
-
-function ScrollAreaVertical() {
- return (
-
-
-
-
- Tags
-
- {tags.map((tag) => (
-
- {tag}
-
-
- ))}
-
-
-
- );
-}
-
-export const works = [
- {
- artist: "Placeholder Name",
- },
- {
- artist: "Placeholder Name",
- },
- {
- artist: "Placeholder Name",
- },
-] as const;
-
-function ScrollAreaHorizontalDemo() {
- return (
-
-
- {works.map((artwork, index) => (
-
-
-
-
-
- Photo by{" "}
-
- {artwork.artist}
-
-
-
- ))}
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/select-demo.tsx b/apps/docs/src/components/docs/primitives/select-demo.tsx
deleted file mode 100644
index b32e4e07..00000000
--- a/apps/docs/src/components/docs/primitives/select-demo.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectLabel,
- SelectTrigger,
- SelectValue,
-} from "ghost-ui";
-import {
- ChartBarIcon,
- ChartLineIcon,
- ChartPieIcon,
- CircleDashed,
-} from "lucide-react";
-import * as React from "react";
-
-export function SelectDemo() {
- return (
-
-
-
-
-
-
-
- Fruits
- Apple
- Banana
- Blueberry
-
- Grapes
-
- Pineapple
-
-
-
-
-
-
-
-
- {Array.from({ length: 100 }).map((_, i) => (
-
- Item {i}
-
- ))}
-
-
-
-
-
-
-
- Apple
- Banana
- Blueberry
-
- Grapes
-
- Pineapple
-
-
-
-
-
-
- With Icon
- >
- }
- />
-
-
-
-
- Line
-
-
-
- Bar
-
-
-
- Pie
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/separator-demo.tsx b/apps/docs/src/components/docs/primitives/separator-demo.tsx
deleted file mode 100644
index 57ca0763..00000000
--- a/apps/docs/src/components/docs/primitives/separator-demo.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Separator } from "ghost-ui";
-
-export function SeparatorDemo() {
- return (
-
-
-
Tailwind CSS
-
- A utility-first CSS framework.
-
-
-
-
-
Blog
-
-
Docs
-
-
Source
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/sheet-demo.tsx b/apps/docs/src/components/docs/primitives/sheet-demo.tsx
deleted file mode 100644
index bef1b5d2..00000000
--- a/apps/docs/src/components/docs/primitives/sheet-demo.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import {
- Button,
- Input,
- Label,
- Sheet,
- SheetClose,
- SheetContent,
- SheetDescription,
- SheetFooter,
- SheetHeader,
- SheetTitle,
- SheetTrigger,
-} from "ghost-ui";
-
-const SHEET_SIDES = ["top", "right", "bottom", "left"] as const;
-
-export function SheetDemo() {
- return (
-
-
-
- Open
-
-
-
- Edit profile
-
- Make changes to your profile here. Click save when you're
- done.
-
-
-
-
- Save changes
-
- Close
-
-
-
-
-
- {SHEET_SIDES.map((side) => (
-
-
-
- {side}
-
-
-
-
- Edit profile
-
- Make changes to your profile here. Click save when you're
- done.
-
-
-
-
- Lorem Ipsum
-
- {Array.from({ length: 10 }).map((_, index) => (
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
- do eiusmod tempor incididunt ut labore et dolore magna
- aliqua. Ut enim ad minim veniam, quis nostrud exercitation
- ullamco laboris nisi ut aliquip ex ea commodo consequat.
- Duis aute irure dolor in reprehenderit in voluptate velit
- esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
- occaecat cupidatat non proident, sunt in culpa qui officia
- deserunt mollit anim id est laborum.
-
- ))}
-
-
- Save changes
-
- Cancel
-
-
-
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/skeleton-demo.tsx b/apps/docs/src/components/docs/primitives/skeleton-demo.tsx
deleted file mode 100644
index 29f92922..00000000
--- a/apps/docs/src/components/docs/primitives/skeleton-demo.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Card, CardContent, CardHeader, Skeleton } from "ghost-ui";
-
-export function SkeletonDemo() {
- return (
-
-
-
- {Array.from({ length: 3 }).map((_, index) => (
-
-
-
-
-
-
-
-
-
- ))}
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/slider-demo.tsx b/apps/docs/src/components/docs/primitives/slider-demo.tsx
deleted file mode 100644
index 3ed8978e..00000000
--- a/apps/docs/src/components/docs/primitives/slider-demo.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-"use client";
-
-import { Label, Slider } from "ghost-ui";
-import * as React from "react";
-
-export function SliderDemo() {
- return (
-
- );
-}
-
-function SliderControlled() {
- const [value, setValue] = React.useState([0.3, 0.7]);
-
- return (
-
-
- Temperature
-
- {value.join(", ")}
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/sonner-demo.tsx b/apps/docs/src/components/docs/primitives/sonner-demo.tsx
deleted file mode 100644
index c2554f29..00000000
--- a/apps/docs/src/components/docs/primitives/sonner-demo.tsx
+++ /dev/null
@@ -1,131 +0,0 @@
-"use client";
-
-import { Button } from "ghost-ui";
-import * as React from "react";
-import { toast } from "sonner";
-
-const promiseCode = "`${data.name} toast has been added`";
-
-const allTypes = [
- {
- name: "Default",
- snippet: `toast('Event has been created')`,
- action: () => toast("Event has been created"),
- },
- {
- name: "Description",
- snippet: `toast.message('Event has been created', {
- description: 'Monday, January 3rd at 6:00pm',
-})`,
- action: () =>
- toast("Event has been created", {
- description: "Monday, January 3rd at 6:00pm",
- }),
- },
- {
- name: "Success",
- snippet: `toast.success('Event has been created')`,
- action: () => toast.success("Event has been created"),
- },
- {
- name: "Info",
- snippet: `toast.info('Be at the area 10 minutes before the event time')`,
- action: () => toast.info("Be at the area 10 minutes before the event time"),
- },
- {
- name: "Warning",
- snippet: `toast.warning('Event start time cannot be earlier than 8am')`,
- action: () => toast.warning("Event start time cannot be earlier than 8am"),
- },
- {
- name: "Error",
- snippet: `toast.error('Event has not been created')`,
- action: () => toast.error("Event has not been created"),
- },
- {
- name: "Action",
- action: () =>
- toast.message("Event has been created", {
- action: {
- label: "Undo",
- onClick: () => console.log("Undo"),
- },
- }),
- },
- {
- name: "Cancel",
- action: () =>
- toast.message("Event has been created", {
- cancel: {
- label: "Cancel",
- onClick: () => console.log("Cancel"),
- },
- }),
- },
- {
- name: "Promise",
- snippet: `const promise = () => new Promise((resolve) => setTimeout(() => resolve({ name: 'Sonner' }), 2000));
-
-toast.promise(promise, {
- loading: 'Loading...',
- success: (data) => {
- return ${promiseCode};
- },
- error: 'Error',
-});`,
- action: () =>
- toast.promise<{ name: string }>(
- () =>
- new Promise((resolve) => {
- setTimeout(() => {
- resolve({ name: "Sonner" });
- }, 2000);
- }),
- {
- loading: "Loading...",
- success: (data) => {
- return `${data.name} toast has been added`;
- },
- error: "Error",
- },
- ),
- },
-];
-
-export function SonnerDemo() {
- const [activeType, setActiveType] = React.useState(allTypes[0]);
- return (
-
- toast("My first toast")} variant="outline">
- Give me a toast
-
-
- toast("Event has been created", {
- description: "Sunday, December 03, 2023 at 9:00 AM",
- action: {
- label: "Undo",
- onClick: () => console.log("Undo"),
- },
- })
- }
- >
- Show Toast
-
- {allTypes.map((type) => (
- {
- type.action();
- setActiveType(type);
- }}
- key={type.name}
- >
- {type.name}
-
- ))}
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/switch-demo.tsx b/apps/docs/src/components/docs/primitives/switch-demo.tsx
deleted file mode 100644
index 33470e81..00000000
--- a/apps/docs/src/components/docs/primitives/switch-demo.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Label, Switch } from "ghost-ui";
-
-export function SwitchDemo() {
- return (
-
-
-
- Airplane Mode
-
-
-
- Bluetooth
-
-
-
-
Share across devices
-
- Focus is shared across devices, and turns off when you leave the
- app.
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/table-demo.tsx b/apps/docs/src/components/docs/primitives/table-demo.tsx
deleted file mode 100644
index 59e18af6..00000000
--- a/apps/docs/src/components/docs/primitives/table-demo.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import {
- Table,
- TableBody,
- TableCaption,
- TableCell,
- TableFooter,
- TableHead,
- TableHeader,
- TableRow,
-} from "ghost-ui";
-
-const invoices = [
- {
- invoice: "INV001",
- paymentStatus: "Paid",
- totalAmount: "$250.00",
- paymentMethod: "Credit Card",
- },
- {
- invoice: "INV002",
- paymentStatus: "Pending",
- totalAmount: "$150.00",
- paymentMethod: "PayPal",
- },
- {
- invoice: "INV003",
- paymentStatus: "Unpaid",
- totalAmount: "$350.00",
- paymentMethod: "Bank Transfer",
- },
- {
- invoice: "INV004",
- paymentStatus: "Paid",
- totalAmount: "$450.00",
- paymentMethod: "Credit Card",
- },
- {
- invoice: "INV005",
- paymentStatus: "Paid",
- totalAmount: "$550.00",
- paymentMethod: "PayPal",
- },
- {
- invoice: "INV006",
- paymentStatus: "Pending",
- totalAmount: "$200.00",
- paymentMethod: "Bank Transfer",
- },
- {
- invoice: "INV007",
- paymentStatus: "Unpaid",
- totalAmount: "$300.00",
- paymentMethod: "Credit Card",
- },
-];
-
-export function TableDemo() {
- return (
-
- A list of your recent invoices.
-
-
- Invoice
- Status
- Method
- Amount
-
-
-
- {invoices.map((invoice) => (
-
- {invoice.invoice}
- {invoice.paymentStatus}
- {invoice.paymentMethod}
- {invoice.totalAmount}
-
- ))}
-
-
-
- Total
- $2,500.00
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/tabs-demo.tsx b/apps/docs/src/components/docs/primitives/tabs-demo.tsx
deleted file mode 100644
index ec679cbd..00000000
--- a/apps/docs/src/components/docs/primitives/tabs-demo.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import {
- Button,
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
- Input,
- Label,
- Tabs,
- TabsContent,
- TabsList,
- TabsTrigger,
-} from "ghost-ui";
-import { AppWindowIcon, CodeIcon } from "lucide-react";
-
-export function TabsDemo() {
- return (
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/textarea-demo.tsx b/apps/docs/src/components/docs/primitives/textarea-demo.tsx
deleted file mode 100644
index 12259ccf..00000000
--- a/apps/docs/src/components/docs/primitives/textarea-demo.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Label, Textarea } from "ghost-ui";
-
-export function TextareaDemo() {
- return (
-
-
-
-
- Label
-
-
-
-
- With label and description
-
-
-
- Type your message and press enter to send.
-
-
-
- Disabled
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/toggle-demo.tsx b/apps/docs/src/components/docs/primitives/toggle-demo.tsx
deleted file mode 100644
index 4ef1c3b0..00000000
--- a/apps/docs/src/components/docs/primitives/toggle-demo.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Toggle } from "ghost-ui";
-import {
- BoldIcon,
- BookmarkIcon,
- ItalicIcon,
- UnderlineIcon,
-} from "lucide-react";
-
-export function ToggleDemo() {
- return (
-
-
-
-
-
-
-
-
- Disabled
-
-
-
- Italic
-
-
-
-
-
- Small
-
-
- Large
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/toggle-group-demo.tsx b/apps/docs/src/components/docs/primitives/toggle-group-demo.tsx
deleted file mode 100644
index 1ac43fe0..00000000
--- a/apps/docs/src/components/docs/primitives/toggle-group-demo.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { ToggleGroup, ToggleGroupItem } from "ghost-ui";
-import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react";
-
-export function ToggleGroupDemo() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- All
-
-
- Missed
-
-
-
-
-
- Last 24 hours
-
-
- Last 7 days
-
-
-
-
-
- Last 24 hours
-
-
- Last 7 days
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/primitives/tooltip-demo.tsx b/apps/docs/src/components/docs/primitives/tooltip-demo.tsx
deleted file mode 100644
index 8be1855a..00000000
--- a/apps/docs/src/components/docs/primitives/tooltip-demo.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Button, Tooltip, TooltipContent, TooltipTrigger } from "ghost-ui";
-import { InfoIcon } from "lucide-react";
-
-export function TooltipDemo() {
- return (
-
-
-
- Hover
-
-
- Add to library
-
-
-
- {["top", "right", "bottom", "left"].map((side) => (
-
-
-
- {side}
-
-
-
- Add to library
-
-
- ))}
-
-
-
-
-
- Info
-
-
-
- To learn more about how this works, check out the docs. If you have
- any questions, please reach out to us.
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/registry-sidebar.tsx b/apps/docs/src/components/docs/registry-sidebar.tsx
deleted file mode 100644
index f83837b7..00000000
--- a/apps/docs/src/components/docs/registry-sidebar.tsx
+++ /dev/null
@@ -1,187 +0,0 @@
-"use client";
-
-import {
- Button,
- Collapsible,
- CollapsibleContent,
- CollapsibleTrigger,
- Input,
- Sidebar,
- SidebarContent,
- SidebarGroup,
- SidebarGroupContent,
- SidebarGroupLabel,
- SidebarHeader,
- SidebarMenu,
- SidebarMenuButton,
- SidebarMenuItem,
- useSidebar,
-} from "ghost-ui";
-import { ChevronDown, Menu, Search } from "lucide-react";
-import { useEffect, useMemo, useState } from "react";
-import { Link, useLocation } from "react-router";
-import {
- type ComponentEntry,
- categories,
- getComponentsByCategory,
-} from "@/lib/component-registry";
-
-const topNav = [
- { name: "home", path: "/" },
- { name: "colors", path: "/ui/foundations/colors" },
- { name: "typography", path: "/ui/foundations/typography" },
- { name: "all components", path: "/ui/components" },
-];
-
-export function RegistrySidebar() {
- const { pathname } = useLocation();
- const { setOpenMobile } = useSidebar();
-
- const [searchTerm, setSearchTerm] = useState("");
-
- const filteredCategories = useMemo(() => {
- const term = searchTerm.toLowerCase();
- return categories
- .map((cat) => {
- const items = getComponentsByCategory(cat.slug);
- const filtered = term
- ? items.filter((item) => item.name.toLowerCase().includes(term))
- : items;
- return { ...cat, items: filtered };
- })
- .filter((cat) => cat.items.length > 0);
- }, [searchTerm]);
-
- const hasSearch = searchTerm.length > 0;
-
- return (
- <>
- setOpenMobile(true)}
- appearance="icon"
- size="lg"
- className="hover:scale-105 sticky z-50 top-4 ml-6 md:hidden"
- >
-
-
-
-
-
-
-
-
- setSearchTerm(e.target.value)}
- />
-
-
-
-
-
- {!hasSearch && (
-
-
-
-
-
- get started
-
-
-
-
-
-
-
-
- {topNav.map((item) => (
-
-
- setOpenMobile(false)}
- >
-
- {item.name}
-
-
-
-
- ))}
-
-
-
-
-
- )}
-
- {filteredCategories.map((cat) => (
- setOpenMobile(false)}
- />
- ))}
-
-
- >
- );
-}
-
-function CategorySection({
- label,
- items,
- pathname,
- defaultOpen,
- onNavigate,
-}: {
- label: string;
- items: ComponentEntry[];
- pathname: string;
- defaultOpen: boolean;
- onNavigate: () => void;
-}) {
- return (
-
-
-
-
- {label}
-
-
-
-
-
-
- {items.map((item) => (
-
-
-
- {item.name}
-
-
-
- ))}
-
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/docs/tooltip-wrapper.tsx b/apps/docs/src/components/docs/tooltip-wrapper.tsx
index 5dbd8e3a..71a32704 100644
--- a/apps/docs/src/components/docs/tooltip-wrapper.tsx
+++ b/apps/docs/src/components/docs/tooltip-wrapper.tsx
@@ -1,6 +1,11 @@
"use client";
-import { cn, Tooltip, TooltipContent, TooltipTrigger } from "ghost-ui";
+import {
+ cn,
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@design-intelligence/vessel";
import { ComponentProps } from "react";
import { useShowTooltips } from "@/store/preferences-store";
diff --git a/apps/docs/src/components/docs/wrappers.tsx b/apps/docs/src/components/docs/wrappers.tsx
index 8d5cf589..4a08dab0 100644
--- a/apps/docs/src/components/docs/wrappers.tsx
+++ b/apps/docs/src/components/docs/wrappers.tsx
@@ -1,4 +1,4 @@
-import { cn } from "ghost-ui";
+import { cn } from "@design-intelligence/vessel";
import { ComponentProps } from "react";
interface SectionWrapperProps extends ComponentProps<"div"> {
diff --git a/apps/docs/src/components/theme-panel/ColorControls.tsx b/apps/docs/src/components/theme-panel/ColorControls.tsx
deleted file mode 100644
index 3add09de..00000000
--- a/apps/docs/src/components/theme-panel/ColorControls.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-"use client";
-
-import {
- DEFAULT_COLORS_DARK,
- DEFAULT_COLORS_LIGHT,
- getResolvedVariable,
- useTheme,
-} from "ghost-ui";
-import { useEffect, useState } from "react";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-import { ColorSwatch } from "./ColorSwatch";
-
-interface ColorGroup {
- label: string;
- variables: { key: string; label: string }[];
-}
-
-const COLOR_GROUPS: ColorGroup[] = [
- {
- label: "Accent",
- variables: [{ key: "--background-accent", label: "Accent" }],
- },
- {
- label: "Surfaces",
- variables: [
- { key: "--background-default", label: "Default" },
- { key: "--background-alt", label: "Alt" },
- { key: "--background-muted", label: "Muted" },
- { key: "--background-medium", label: "Medium" },
- { key: "--background-inverse", label: "Inverse" },
- ],
- },
- {
- label: "Text",
- variables: [
- { key: "--text-default", label: "Default" },
- { key: "--text-muted", label: "Muted" },
- { key: "--text-alt", label: "Alt" },
- { key: "--text-inverse", label: "Inverse" },
- ],
- },
- {
- label: "Borders",
- variables: [
- { key: "--border-default", label: "Default" },
- { key: "--border-input", label: "Input" },
- { key: "--border-card", label: "Card" },
- { key: "--border-strong", label: "Strong" },
- ],
- },
- {
- label: "Feedback",
- variables: [
- { key: "--background-danger", label: "Danger" },
- { key: "--background-success", label: "Success" },
- { key: "--background-warning", label: "Warning" },
- { key: "--background-info", label: "Info" },
- ],
- },
- {
- label: "Charts",
- variables: [
- { key: "--chart-1", label: "Chart 1" },
- { key: "--chart-2", label: "Chart 2" },
- { key: "--chart-3", label: "Chart 3" },
- { key: "--chart-4", label: "Chart 4" },
- { key: "--chart-5", label: "Chart 5" },
- ],
- },
-];
-
-// Linked variables: when accent changes, also update these
-const ACCENT_LINKED = ["--border-accent", "--text-accent"];
-
-export function ColorControls() {
- const { overrides, setVariable, setVariables } = useThemePanel();
- const { resolvedTheme } = useTheme();
- const isDark = resolvedTheme === "dark";
- const defaults = isDark ? DEFAULT_COLORS_DARK : DEFAULT_COLORS_LIGHT;
-
- // Track resolved values for initial display
- const [resolved, setResolved] = useState>({});
-
- useEffect(() => {
- // Read current computed values for all color variables
- const vals: Record = {};
- for (const group of COLOR_GROUPS) {
- for (const v of group.variables) {
- const computed = getResolvedVariable(v.key);
- if (computed) {
- vals[v.key] = rgbToHex(computed) || defaults[v.key] || "#000000";
- }
- }
- }
- setResolved(vals);
- }, [overrides, isDark]); // eslint-disable-line react-hooks/exhaustive-deps
-
- function getColor(key: string): string {
- return overrides[key] || resolved[key] || defaults[key] || "#000000";
- }
-
- function handleChange(key: string, value: string) {
- if (key === "--background-accent") {
- // Also update linked accent variables
- const linked: Record = { [key]: value };
- for (const k of ACCENT_LINKED) {
- linked[k] = value;
- }
- setVariables(linked);
- } else {
- setVariable(key, value);
- }
- }
-
- return (
-
- {COLOR_GROUPS.map((group) => (
-
-
- {group.label}
-
-
- {group.variables.map((v) => (
- handleChange(v.key, val)}
- label={v.label}
- />
- ))}
-
-
- ))}
-
- );
-}
-
-function rgbToHex(rgb: string): string | null {
- // Handle hex passthrough
- if (rgb.startsWith("#")) return rgb;
-
- // Handle "rgb(r, g, b)" or "r g b" format
- const match = rgb.match(/(\d+)[,\s]+(\d+)[,\s]+(\d+)/);
- if (!match) return null;
-
- const r = parseInt(match[1]);
- const g = parseInt(match[2]);
- const b = parseInt(match[3]);
- return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
-}
diff --git a/apps/docs/src/components/theme-panel/ColorSwatch.tsx b/apps/docs/src/components/theme-panel/ColorSwatch.tsx
deleted file mode 100644
index 22acfeb5..00000000
--- a/apps/docs/src/components/theme-panel/ColorSwatch.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-"use client";
-
-interface ColorSwatchProps {
- value: string;
- onChange: (value: string) => void;
- label: string;
-}
-
-export function ColorSwatch({ value, onChange, label }: ColorSwatchProps) {
- return (
-
-
-
onChange(e.target.value)}
- className="absolute inset-0 w-[200%] h-[200%] -top-1/2 -left-1/2 cursor-pointer opacity-0"
- />
-
-
-
- {label}
-
- {value}
-
-
-
- );
-}
diff --git a/apps/docs/src/components/theme-panel/ExportReset.tsx b/apps/docs/src/components/theme-panel/ExportReset.tsx
deleted file mode 100644
index 27c40d16..00000000
--- a/apps/docs/src/components/theme-panel/ExportReset.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-"use client";
-
-import { Button } from "ghost-ui";
-import { Check, Copy, RotateCcw } from "lucide-react";
-import { useCallback, useState } from "react";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-
-export function ExportReset() {
- const { exportCSS, reset, overrides } = useThemePanel();
- const [copied, setCopied] = useState(false);
-
- const handleCopy = useCallback(async () => {
- const css = exportCSS();
- if (!css) return;
-
- await navigator.clipboard.writeText(css);
- setCopied(true);
- setTimeout(() => setCopied(false), 2000);
- }, [exportCSS]);
-
- const hasOverrides = Object.keys(overrides).length > 0;
-
- return (
-
-
- {copied ? (
- <>
-
- Copied
- >
- ) : (
- <>
-
- Copy CSS
- >
- )}
-
-
-
- Reset
-
-
- );
-}
diff --git a/apps/docs/src/components/theme-panel/PresetSelector.tsx b/apps/docs/src/components/theme-panel/PresetSelector.tsx
deleted file mode 100644
index adaed674..00000000
--- a/apps/docs/src/components/theme-panel/PresetSelector.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-"use client";
-
-import { cn, PRESETS } from "ghost-ui";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-
-export function PresetSelector() {
- const { activePresetId, applyPreset } = useThemePanel();
-
- return (
-
- {PRESETS.map((preset) => {
- const isActive = activePresetId === preset.id;
- return (
-
applyPreset(preset.id)}
- className={cn(
- "flex flex-col items-center gap-2 rounded-card-sm border p-3 transition-all cursor-pointer",
- isActive
- ? "border-border-accent shadow-mini bg-background-alt"
- : "border-border hover:border-border-input-hover hover:bg-background-alt",
- )}
- >
-
- {[
- preset.preview.accent,
- preset.preview.background,
- preset.preview.text,
- preset.preview.muted,
- ].map((color, i) => (
-
- ))}
-
-
- {preset.name}
-
-
- );
- })}
- {/* Custom indicator */}
- {activePresetId === null && (
-
- )}
-
- );
-}
diff --git a/apps/docs/src/components/theme-panel/RadiusControls.tsx b/apps/docs/src/components/theme-panel/RadiusControls.tsx
deleted file mode 100644
index 63f2c3fb..00000000
--- a/apps/docs/src/components/theme-panel/RadiusControls.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-"use client";
-
-import { Slider } from "ghost-ui";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-
-export function RadiusControls() {
- const { radiusScale, setRadiusScale } = useThemePanel();
-
- const label =
- radiusScale < 10
- ? "Sharp"
- : radiusScale < 40
- ? "Subtle"
- : radiusScale < 60
- ? "Default"
- : radiusScale < 85
- ? "Rounded"
- : "Pill";
-
- return (
-
-
-
-
- Corner Radius
-
-
- {label}
-
-
-
setRadiusScale(v)}
- min={0}
- max={100}
- step={1}
- />
-
-
- {/* Live preview */}
-
-
Preview
-
-
- Card
-
-
- Button
-
-
- Input
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/theme-panel/ShadowControls.tsx b/apps/docs/src/components/theme-panel/ShadowControls.tsx
deleted file mode 100644
index bed7961d..00000000
--- a/apps/docs/src/components/theme-panel/ShadowControls.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-"use client";
-
-import { Slider } from "ghost-ui";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-
-export function ShadowControls() {
- const { shadowScale, setShadowScale } = useThemePanel();
-
- const label =
- shadowScale < 5
- ? "None"
- : shadowScale < 30
- ? "Subtle"
- : shadowScale < 60
- ? "Default"
- : shadowScale < 85
- ? "Prominent"
- : "Heavy";
-
- return (
-
-
-
-
- Shadow Intensity
-
-
- {label}
-
-
-
setShadowScale(v)}
- min={0}
- max={100}
- step={1}
- />
-
-
- {/* Live preview */}
-
-
Preview
-
-
- Mini
-
-
- Card
-
-
- Elevated
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/theme-panel/ThemePanel.tsx b/apps/docs/src/components/theme-panel/ThemePanel.tsx
deleted file mode 100644
index 8a8571e3..00000000
--- a/apps/docs/src/components/theme-panel/ThemePanel.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-"use client";
-
-import {
- cn,
- Separator,
- Tabs,
- TabsContent,
- TabsList,
- TabsTrigger,
-} from "ghost-ui";
-import { XIcon } from "lucide-react";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-import { ColorControls } from "./ColorControls";
-import { ExportReset } from "./ExportReset";
-import { PresetSelector } from "./PresetSelector";
-import { RadiusControls } from "./RadiusControls";
-import { ShadowControls } from "./ShadowControls";
-import { TypographyControls } from "./TypographyControls";
-
-export function ThemePanel() {
- const { isOpen, setOpen } = useThemePanel();
-
- return (
-
- {/* Header */}
-
-
-
- Theme
-
- setOpen(false)}
- className="rounded-xs opacity-70 transition-opacity hover:opacity-100"
- >
-
- Close
-
-
-
- Customize the design language in real time.
-
-
-
- {/* Scrollable content */}
-
- {/* Presets */}
-
-
-
-
- {/* Token Controls */}
-
-
-
- Colors
-
-
- Radius
-
-
- Shadows
-
-
- Type
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Footer */}
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/theme-panel/ThemePanelTrigger.tsx b/apps/docs/src/components/theme-panel/ThemePanelTrigger.tsx
deleted file mode 100644
index b075f1f6..00000000
--- a/apps/docs/src/components/theme-panel/ThemePanelTrigger.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-"use client";
-
-import { Button } from "ghost-ui";
-import { Palette } from "lucide-react";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-
-export function ThemePanelTrigger() {
- const { setOpen } = useThemePanel();
-
- return (
- setOpen(true)}
- className="bg-background-inverse text-primary-foreground hover:scale-105"
- >
-
-
- );
-}
diff --git a/apps/docs/src/components/theme-panel/TypographyControls.tsx b/apps/docs/src/components/theme-panel/TypographyControls.tsx
deleted file mode 100644
index c2d2467c..00000000
--- a/apps/docs/src/components/theme-panel/TypographyControls.tsx
+++ /dev/null
@@ -1,194 +0,0 @@
-"use client";
-
-import { Slider } from "ghost-ui";
-import { useCallback, useState } from "react";
-import { useThemePanel } from "@/contexts/ThemePanelContext";
-
-// Default values for scaling (50 = default)
-const WEIGHT_MAP: Record> = {
- 0: {
- "--heading-display-font-weight": "300",
- "--heading-section-font-weight": "400",
- "--heading-sub-font-weight": "400",
- "--heading-card-font-weight": "400",
- },
- 25: {
- "--heading-display-font-weight": "600",
- "--heading-section-font-weight": "500",
- "--heading-sub-font-weight": "500",
- "--heading-card-font-weight": "500",
- },
- 50: {
- "--heading-display-font-weight": "900",
- "--heading-section-font-weight": "700",
- "--heading-sub-font-weight": "700",
- "--heading-card-font-weight": "600",
- },
- 75: {
- "--heading-display-font-weight": "900",
- "--heading-section-font-weight": "900",
- "--heading-sub-font-weight": "700",
- "--heading-card-font-weight": "700",
- },
- 100: {
- "--heading-display-font-weight": "900",
- "--heading-section-font-weight": "900",
- "--heading-sub-font-weight": "900",
- "--heading-card-font-weight": "900",
- },
-};
-
-function interpolateWeight(factor: number): Record {
- const keys = Object.keys(WEIGHT_MAP)
- .map(Number)
- .sort((a, b) => a - b);
- const lower = keys.filter((k) => k <= factor).pop() ?? 0;
- const upper = keys.find((k) => k >= factor) ?? 100;
-
- if (lower === upper) return WEIGHT_MAP[lower];
-
- // Snap to nearest weight bracket
- const t = (factor - lower) / (upper - lower);
- return t < 0.5 ? WEIGHT_MAP[lower] : WEIGHT_MAP[upper];
-}
-
-const TRACKING_VARS = [
- "--heading-display-letter-spacing",
- "--heading-section-letter-spacing",
- "--heading-sub-letter-spacing",
- "--heading-card-letter-spacing",
-];
-
-const DEFAULT_TRACKING = [-0.05, -0.035, -0.02, -0.01];
-
-export function TypographyControls() {
- const { setVariables } = useThemePanel();
- const [weightScale, setWeightScale] = useState(50);
- const [trackingScale, setTrackingScale] = useState(50);
-
- const handleWeightChange = useCallback(
- (value: number) => {
- setWeightScale(value);
- setVariables(interpolateWeight(value));
- },
- [setVariables],
- );
-
- const handleTrackingChange = useCallback(
- (value: number) => {
- setTrackingScale(value);
- // 0 = loose (0em), 50 = default, 100 = very tight (2x default)
- const multiplier = value / 50;
- const vars: Record = {};
- TRACKING_VARS.forEach((key, i) => {
- vars[key] = `${(DEFAULT_TRACKING[i] * multiplier).toFixed(3)}em`;
- });
- setVariables(vars);
- },
- [setVariables],
- );
-
- const weightLabel =
- weightScale < 20
- ? "Light"
- : weightScale < 45
- ? "Medium"
- : weightScale < 65
- ? "Default"
- : weightScale < 85
- ? "Bold"
- : "Black";
-
- const trackingLabel =
- trackingScale < 15
- ? "Loose"
- : trackingScale < 40
- ? "Normal"
- : trackingScale < 60
- ? "Default"
- : trackingScale < 85
- ? "Tight"
- : "Very Tight";
-
- return (
-
-
-
-
- Weight
-
-
- {weightLabel}
-
-
-
handleWeightChange(v)}
- min={0}
- max={100}
- step={1}
- />
-
-
-
-
-
- Letter Spacing
-
-
- {trackingLabel}
-
-
-
handleTrackingChange(v)}
- min={0}
- max={100}
- step={1}
- />
-
-
- {/* Live preview */}
-
-
Preview
-
-
- Heading
-
-
- Body text sample for preview.
-
-
-
-
- );
-}
diff --git a/apps/docs/src/components/theme/ThemeControls.tsx b/apps/docs/src/components/theme/ThemeControls.tsx
deleted file mode 100644
index cba10256..00000000
--- a/apps/docs/src/components/theme/ThemeControls.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-"use client";
-
-import { ThemeToggle } from "ghost-ui";
-import { ThemePanel } from "@/components/theme-panel/ThemePanel";
-import { ThemePanelTrigger } from "@/components/theme-panel/ThemePanelTrigger";
-import { ThemePanelProvider } from "@/contexts/ThemePanelContext";
-
-export function ThemeControls() {
- return (
-
-
- {/* */}
-
-
- {/* */}
-
- );
-}
diff --git a/apps/docs/src/content/docs/checks-and-review.mdx b/apps/docs/src/content/docs/checks-and-review.mdx
new file mode 100644
index 00000000..b2f0caac
--- /dev/null
+++ b/apps/docs/src/content/docs/checks-and-review.mdx
@@ -0,0 +1,133 @@
+---
+title: Checks And Review
+description: Opt in to review assertions with the checks directory, bind them to fingerprint nodes, and assemble advisory review packets from a diff.
+kicker: Docs
+section: guide
+order: 25
+slug: checks-and-review
+---
+
+
+
+`gather` and `pull` are feed-forward: they ground your agent **before** it
+builds. Checks and `ghost review` are the optional feed-back half: **after** a
+change exists, they help the same agent review it against the truths you wrote
+down.
+
+The division of labor stays the same. The CLI assembles deterministically — it
+reads a diff, matches touched files to node `materials`, and offers the relevant
+checks. Your agent weighs the packet and renders findings. Ghost grades nothing.
+
+This guide assumes you have a fingerprint; if not, start with
+[Getting Started](/docs/getting-started). Exact flags and exit codes live in
+the [CLI Reference](/docs/cli).
+
+```text
+Review this diff against the Ghost checks.
+```
+
+
+
+
+
+Checks live in a flat `.ghost/checks/` directory. Two properties keep them
+honest:
+
+- **Opt-in.** Core `ghost init` ships no checks. You add them explicitly.
+- **Feed-back only.** Nothing under `checks/` is a node. `ghost gather` and
+ `ghost pull` never emit check content, so checks cannot leak into generation
+ context.
+
+```bash
+ghost checks init # scaffold .ghost/checks/
+ghost init --with checks # or opt in at scaffold time
+```
+
+`ghost validate` validates checks along with the rest of the package, and
+ordinary Git review is the approval boundary for check changes, same as any
+fingerprint edit.
+
+
+
+
+
+A check is one markdown file under `.ghost/checks/`: a review assertion
+bound to the fingerprint nodes it guards.
+
+```markdown
+---
+name: logo-clearspace-holds
+description: Logo usage preserves clearspace, lockup integrity, and glyph rules.
+severity: medium
+references:
+ - asset.logo
+---
+
+Grade whether the change preserves the logo guidance in `asset.logo`. Flag
+compressed clearspace, altered lockups, stretched marks, or cases where the
+glyph is used when the full lockup is required.
+```
+
+| Property | What it does |
+| --- | --- |
+| `name` | The check's identity. |
+| `description` | What the check asserts, in one line. |
+| `severity` | `high`, `medium`, or `low` — advice for the reviewing agent, not an enforcement level. |
+| `references` | Node ids this check is grounded in, optionally scoped to one section with a heading anchor (`asset.logo > Clearspace`). |
+
+The prose body is the instruction the reviewing agent follows. Write it like a
+review comment you would trust another engineer to act on: what to grade, what
+to flag, and what counts as preserved.
+
+`references` does double duty. It grounds the check — `ghost validate` verifies
+every reference resolves to a real node — and it routes the check: when a diff
+touches a file matched by a referenced node's `materials`, that check is
+offered. A check that references no material-backed node is **always offered**,
+which is the right shape for cross-cutting assertions like voice or tone.
+
+
+
+
+
+`ghost review` requires the checks directory and a diff. By default it diffs the
+worktree against `HEAD`; point it elsewhere with `--base` or feed it a diff
+directly:
+
+```bash
+ghost review # worktree vs HEAD
+ghost review --base main # vs another ref
+git diff main | ghost review --diff=- # from stdin
+ghost review --format json # raw packet for host tooling
+```
+
+The advisory packet contains:
+
+- **Touched files** parsed from the diff.
+- **Matched material-backed nodes** — every node whose repo-relative `materials`
+ locators match a touched file, with its full prose and which locators matched.
+ (HTTPS locators identify external material; they never match diff paths.)
+- **Offered checks** — checks routed via a matched node (`matched`) plus checks
+ with no material-backed references (`always`), each with its prose and the
+ referenced nodes' baseline prose.
+- **Coverage gaps** — touched files no node's materials claim, and
+ material-backed nodes no check guards.
+
+The host agent takes it from there: weigh which checks apply, cite the
+fingerprint prose, and do not invent obligations the fingerprint never states.
+
+
+
+
+
+Coverage gaps are the review-side analog of `ghost pulse`: signals for the
+fingerprint's author, not errors.
+
+- **Unmatched files** — the diff touched something no node claims. Maybe that
+ surface deserves a truth, or maybe it is genuinely out of scope.
+- **Unguarded materials** — a node points at concrete material, but no check
+ guards it. The truth feeds generation but nothing asserts it survives review.
+
+Grow coverage the same way you grow the fingerprint: one decision at a time,
+starting with the drift you keep catching by hand.
+
+
diff --git a/apps/docs/src/content/docs/cli-reference.mdx b/apps/docs/src/content/docs/cli-reference.mdx
index 2fed608b..8151abe1 100644
--- a/apps/docs/src/content/docs/cli-reference.mdx
+++ b/apps/docs/src/content/docs/cli-reference.mdx
@@ -1,6 +1,6 @@
---
title: CLI Reference
-description: Commands around the portable fingerprint lifecycle. Your agent handles the composition work.
+description: The deterministic commands around the flat fingerprint lifecycle. Your agent does the reading, writing, and interpreting.
kicker: Docs
section: guide
order: 30
@@ -9,198 +9,238 @@ slug: cli
-The CLI does the repeatable parts around the fingerprint lifecycle: create
-packages, report contribution state, validate files, gather optional source material,
-emit handoff packets, govern diffs, compare packages, and record intent. Your
-agent does the reading, writing, and reviewing.
+The CLI does the repeatable parts around the fingerprint lifecycle: scaffold a
+package, validate it, and emit the menu of brand truths. Your agent does the
+interpretation.
-`ghost --help` intentionally shows the short core workflow for new adopters.
-Run `ghost --help --all` for the complete command index; command-specific help
-remains available with `ghost --help`.
+`ghost --help` shows the short core workflow. `ghost --help --all` shows the
+complete command index, and `ghost --help` shows flags for one
+command.
-Canonical Ghost fingerprints start here, with optional child packages for scoped
-product areas:
+The canonical fingerprint is a `.ghost/` directory: a flat set of prose nodes.
```text
-.ghost/manifest.yml
-.ghost/intent.yml
-.ghost/inventory.yml
-.ghost/composition.yml
-.ghost/validate.yml
-apps/checkout/.ghost/manifest.yml
+.ghost/
+ manifest.yml # schema + id
+ glossary.md # the kind vocabulary + what each kind means
+ ..md # a brand truth of a declared kind (principle.density.md)
+ .md # a brand truth without a kind (voice.md)
+ checks/ # optional review assertions; never nodes
```
The command tables below are generated from the CLI source. Run
`pnpm dump:cli-help` after command or flag changes.
+#### Exit codes
+
+Every command follows one contract, so an agent can branch on the exit code:
+
+| Code | Meaning |
+| --- | --- |
+| `0` | Success. |
+| `1` | The command ran but the result is unhappy: `validate` found issues, or an unexpected error was thrown. |
+| `2` | The command was called wrong: a bad flag or argument, a missing package for a command that needs one (`gather`), a `pull` with no known ids, or `review` without a checks directory. |
+| `3` | A command-specific refusal, currently only `skill install` when a skill is already present (pass `--force`). |
+
+A missing package is reported, not treated as a crash: `validate` records it as
+a finding (exit `1`).
+
-### Initialize - `init`
+### Initialize: `init`
-Create a `.ghost/` package with a manifest, raw facet files,
-and deterministic checks. Use `--scope ` for nested package roots. Use
-`--monorepo` to create or preserve the root package, detect workspace child
-roots, and print scoped init commands; add `--apply` to create the detected
-child packages. Use
-`GHOST_PACKAGE_DIR=` only when a host wrapper stores Ghost package
-roots under a different safe relative directory; raw `ghost` defaults to
-`.ghost`. Exact `--package ` values win over the environment default.
+Scaffold a `.ghost/` package with the steering starter: a manifest, a starter `glossary.md`, a core `index.md` node, and demo nodes for stance, composition, anti-goals, patterns, exemplars, materials, and decisions. The demo content is there for inspiration and guidance. Replace it with real product truth before using it to steer generation. Use `--template minimal` when you only want the small manifest/glossary/index starter. Add truths by adding files (`principle.density.md`). Use
+`--package ` for an exact directory, or set `GHOST_PACKAGE_DIR` when a host
+wrapper stores Ghost files outside the default `.ghost`. Core `init` scaffolds
+the fingerprint only; pass `--with checks` to also add the checks directory in
+one move.
```bash
ghost init
-ghost init --monorepo
-ghost init --monorepo --apply
-ghost init --scope apps/checkout
-ghost init --package product-surface
-ghost init --package .design/custom-ghost
+ghost init --template minimal
+ghost init --with checks
+ghost init --package product-fingerprint
GHOST_PACKAGE_DIR=.agents/ghost ghost init
-GHOST_PACKAGE_DIR=.design/memory ghost init --scope apps/checkout
```
-### Contribution facets - `scan`
+### Manage checks: `checks`
-Report whether `manifest.yml` is present and which sparse facets
-this package contributes: `intent`, `inventory`, `composition`, and `validate`.
-Raw repo signals do not count toward inventory contribution.
+**Checks** are optional review assertions in a flat `.ghost/checks/`
+directory. They are feed-back only — they never leak into generation context,
+and they are never emitted by `gather` or `pull`. They are consumed by
+`ghost review`.
-
+
```bash
-ghost scan
-ghost scan --format json
-GHOST_PACKAGE_DIR=.agents/ghost ghost scan --format json
-GHOST_PACKAGE_DIR=.design/memory ghost scan --include-nested --format json
+ghost checks init
```
-### Stack inspection - `stack`
+To remove checks, delete the `.ghost/checks/` directory; like every
+fingerprint edit, ordinary Git review is the approval boundary.
+
+
+
+
-Inspect the root-to-leaf fingerprint stack for one or more paths.
+### Validation: `validate`
-
+Validate the package: artifact shape, per-node validity, material locators,
+check references, and that each node's kind prefix is
+declared in `glossary.md`. An undeclared prefix is a warning with a "did you
+mean" suggestion, not a failure. Defaults to `.ghost`; pass a file to validate
+a single node or check.
+
+
```bash
-ghost stack apps/checkout/review/page.tsx --format json
+ghost validate
+ghost validate .ghost/principle.trust.md
+ghost validate --format json
```
-### Inspect repo signals - `signals`
+
+
+
+
+### Emit the menu: `gather`
-Emit raw signals about a frontend repo as JSON. Use this as scratch evidence
-while authoring curated fingerprint facets.
+`gather` emits the **menu**: every node's id, kind, and description. It does no
+selection — the agent reads the ask against the menu and pulls the truths it
+judges relevant. There is no slice, no anchor, and no inheritance between nodes;
+the package is flat.
-
+
```bash
-ghost signals .
+ghost gather "checkout confirmation email"
+ghost gather "checkout confirmation email" --format json
```
-
+This is the pre-generation step, and "the right context at the right time": the
+agent selects just-in-time against the actual task, then manifests the truths
+into whatever it is building — product UI, marketing, email, a landing page.
-
+### Read the selected truths: `pull`
-### Validation - `lint`
+`pull` emits the named nodes' full prose bodies. It is how an agent reads the
+truths it selected from the menu — preferred over opening the files directly,
+because gather and pull append structured JSONL events to `.ghost/.events`.
-Validate a root `.ghost` fingerprint package or an individual split artifact.
-`--all` validates every nested package and merged stack.
+That file is an events tape for the fingerprint's author: tweak a node's
+description, re-run the task, and `ghost pulse` shows whether the truth appeared
+on the menu, got pulled, was missed by name, or stayed cold. It is a dotfile
+(never a node), gitignored by the scaffold, and safe to delete at any time. The
+events tape is local tuning signal, not canonical fingerprint state.
-
+
```bash
-ghost lint
-ghost lint .ghost/intent.yml
-ghost lint .ghost/validate.yml --format json
-ghost lint --all
+ghost pull principle.trust voice
+ghost pull principle.trust --format json
+ghost pull voice --no-events
```
-### Package fidelity - `verify`
+### Read the local signal: `pulse`
-Validate fingerprint evidence and exemplar paths, typed check refs, and
-optional rationale files.
+`pulse` summarizes the events tape: gathers, pulls, abandoned gathers,
+exposure-normalized hit rates, cold nodes, and unknown ids the agent tried to
+pull.
-
+
```bash
-ghost verify .ghost --root .
-GHOST_PACKAGE_DIR=.design/memory ghost verify --all
+ghost pulse
+ghost pulse --format json
```
-### Reusable Review Command - `emit`
+A mixed pull partially succeeds: known ids are emitted, unknown ids warn with closest-id hints, and misses are recorded for `pulse`. A pull with no known ids exits `2` after recording the miss.
-Emit `review-command` from split fingerprint facets when a host wants a
-reusable review prompt.
+
-
+
-### Agent Context - `relay gather`
+### Assemble a review packet: `review`
-Gather Relay context for a target path or structured Relay request. Relay loads
-config first; omitted `base` means `base.kind: fingerprint`, while
-`base.kind: none` lets agent-framework repos gather declared request context
-without a `.ghost` package. For agents and host adapters, use JSON: the full
-`ghost.relay.gather/v2` result is the stable contract, and its nested `context`
-is `ghost.relay-context/v1`. Plain markdown output remains a compact human
-preview.
+Where `gather` and `pull` are feed-forward, `review` is feed-back. It reads a
+diff, matches touched files to node `materials` (repo-relative locators only),
+offers the relevant checks, and emits an **advisory packet** for the host agent
+to weigh. The command grades nothing; the agent renders findings.
-
+`review` requires the checks directory (`ghost checks init`). Without it, the
+command exits `2` with a pointer to add it. For the authoring model behind
+checks — how to write them and how routing works — see
+[Checks And Review](/docs/checks-and-review).
+
+
```bash
-ghost relay gather apps/checkout/review/page.tsx --format json
-ghost relay gather apps/checkout/review/page.tsx --package product-surface --format json
-ghost relay gather apps/checkout/review/page.tsx --config .ghost/relay.yml --format json
-GHOST_RELAY_CONFIG=.agents/ghost/relay.yml ghost relay gather --request-stdin --format json
-ghost relay gather stacks/portal.renewal-reminder.email.yml --config .agents/ghost/relay.yml --format json
-ghost relay gather --request request.yml --format json
-ghost relay gather --request-stdin --format json
-ghost relay gather apps/checkout/review/page.tsx # human preview
+ghost review # diff the worktree against HEAD
+ghost review --base main # diff against another ref
+git diff main | ghost review --diff=- # read a diff from stdin
+ghost review --format json # raw JSON packet for host tooling
```
-### Inspection - `describe`
+The packet contains:
-Print a markdown section map.
+- **Touched files** parsed from the diff.
+- **Matched material-backed nodes**: every node whose `materials` locators match
+ a touched file, with its full prose and which locators matched.
+- **Offered checks**: checks whose `references` point at a matched node
+ (`matched`), plus checks that reference no material-backed node at all and are
+ therefore always offered (`always`). Each check carries its prose and the
+ referenced nodes' baseline prose.
+- **Coverage gaps**: touched files no node's materials claim, and material-backed
+ nodes no check guards — signals for tuning the fingerprint, not failures.
-
+Each check is one markdown file under `.ghost/checks/`:
-### Survey/cache ops - `survey `
+```markdown
+---
+name: logo-clearspace-holds
+description: Logo usage preserves clearspace, lockup integrity, and glyph rules.
+severity: medium
+references:
+ - asset.logo
+---
-Operate on `ghost.survey/v1` files as compatibility cache source material.
+Grade whether the change preserves the logo guidance in `asset.logo`.
+```
-
+`references` binds the check to fingerprint node ids, optionally scoped to one
+section with a heading anchor (`asset.logo > Clearspace`); `validate` verifies
+every reference resolves. `severity` is one of `high`, `medium`, or `low` —
+advice for the reviewing agent, not an enforcement level.
-
+
-### Deterministic gates - `check`
+### Install the skill: `skill`
-Run active `ghost.validate/v1` gates against a git diff. Without `--package`,
-Ghost groups changed files by resolved fingerprint stack and runs merged checks
-per group.
+Install the Ghost skill bundle so a host agent knows how to author and use the
+fingerprint.
-
+
-### Advisory governance packet - `review`
-
-Emit an evidence-routed advisory review packet grounded in selected context,
-validation checks, and the diff.
-
-
-
-### Comparison - `compare`
-
-Pairwise distance or composite analysis over fingerprint packages.
+```bash
+ghost skill install
+ghost skill install --agent claude
+ghost skill install --dest ~/.codex/skills/ghost
+```
-
+### Command manifest: `manifest`
-### Drift stance - `ack` / `track` / `diverge`
+Emit a self-describing JSON manifest of every command and flag.
-Record how the repo should treat tracked fingerprint drift. These compatibility
-governance verbs still operate on tracked direct fingerprint markdown files.
+
-
-
-
+```bash
+ghost manifest --format json
+```
diff --git a/apps/docs/src/content/docs/fingerprint-authoring.mdx b/apps/docs/src/content/docs/fingerprint-authoring.mdx
index 6f285361..27d639c5 100644
--- a/apps/docs/src/content/docs/fingerprint-authoring.mdx
+++ b/apps/docs/src/content/docs/fingerprint-authoring.mdx
@@ -1,6 +1,6 @@
---
title: Fingerprint Authoring
-description: Co-author Ghost fingerprints with human intent, repo evidence, agent synthesis, and Git review.
+description: Co-author Ghost brand fingerprints through elicitation — human intent, shown material, agent synthesis, and Git review.
kicker: Docs
section: guide
order: 20
@@ -9,143 +9,164 @@ slug: fingerprint-authoring
-A Ghost fingerprint is not a scan dump. It is durable product-surface
-composition that a human and agent shape together.
+A Ghost fingerprint is not a scan dump. It is a set of durable brand truths that
+a human and agent shape together — through **elicitation, not scanning**.
-The human names the intent: what the product surface should feel like, who it
-serves, which situations matter, and what should not drift. Repo scans provide
-evidence: components, routes, docs, stories, copy, screenshots, tokens,
-examples, and UI library references. The agent synthesizes drafts, but ordinary
-Git review is where fingerprint edits become canonical.
+The human names the intent: what the brand should feel like, who it serves,
+which truths are universal, which hold only under a condition, and what should
+not drift. The evidence is what the human says, shows, and points at — words,
+screenshots, links, exemplar products, brand docs, copy they love or hate. The
+agent synthesizes drafts, but ordinary Git review is where fingerprint edits
+become canonical.
+
+Repo-bound reality — components, tokens, paths, building blocks — can be
+recorded as `materials` on the node whose prose explains their purpose. What a
+codebase does is not what a brand means; what it repeats may be legacy, not
+stance.
-Start by classifying the authoring scenario. The scenario determines how much
-weight to give human intent, existing code, and library evidence.
+Start by classifying the authoring scenario. The scenario determines what the
+human can articulate, show, or point at — and how the interview should run.
| Scenario | Authoring posture |
| --- | --- |
-| Net new repo | Human-led. Capture intent, audience, posture, and early anti-goals before inventory grows. |
-| Net new repo + UI library | Human-led with library evidence. Explain how this product uses the library. |
-| Existing repo | Human + scan. Find repeated patterns and exemplars, then ask which ones are canonical. |
-| Existing repo with mixed quality | Curated scan. Separate durable surface composition from legacy debt and accidental repetition. |
-| Design system or UI library | Grammar-led. Describe primitives, tokens, component behavior, accessibility, and composition constraints. |
-| Rebrand, redesign, or migration | Human-led transition. Capture current, target, and migration cautions. |
-| Prototype becoming product | Ratification-led. Preserve only the emergent patterns humans want to keep. |
-| Fork, white label, or tenant variant | Shared base + local divergence. Keep common surface composition broad and local differences scoped. |
-| Monorepo or nested surfaces | Stack-aware. Use root guidance for broad composition and nested packages for surfaces assessed differently. |
+| Net new brand | Stance-first. Elicit feel, audience, and early anti-goals before anything else exists to point at. |
+| Established brand, first fingerprint | Artifact-rich interview. The human can show a lot — shipped surfaces, brand docs, campaigns. Elicit which of it is intentional. |
+| Strong opinions, weak articulation | Example-led. Work from exemplars and counter-exemplars ("this feels like us, this never would") toward the stance underneath. |
+| Brand doc or design-language deck exists | Distillation. The document is testimony, not truth: pull the claims that steer decisions, drop the filler, have the human ratify each survivor. |
+| Rebrand, redesign, or migration | Transition-led. Capture current, target, and what must not be lost in between. |
+| Fork, white label, tenant variant | Shared base + local divergence. Keep common truths broad; scope divergence with conditions. |
-Ghost supports two agent authoring modes:
-
-- **Default** - interview first, scan as needed, draft facet edits, then
- curate.
-- **Auto-draft** - scan first, draft a small starter fingerprint, then curate
- the claims with a human.
-
-Auto-draft is a skill workflow, not a Ghost CLI command. Ask for it in plain
-English:
+Ask for it in plain English:
```text
-Set up the Ghost fingerprint for this repo with auto-draft.
+Set up the Ghost fingerprint for this repo.
```
-1. **Interview** - ask what the product should feel like, who it serves, which
- surfaces show it at its best, and which existing patterns are accidental or
- legacy. In auto-draft mode, use this step after the starter draft to curate
- claims.
-2. **Scan** - inspect routes, components, stories, tests, docs, screenshots,
- copy, tokens, assets, and UI library references.
-3. **Draft** - write the smallest useful `intent.yml`, `inventory.yml`, and
- `composition.yml` entries.
-4. **Curate** - have the human keep, soften, reject, scope, or record important
- claims before treating them as durable surface context.
+1. **Interview** - ask what the brand should feel like, what it should never
+ become, who it serves, and which surfaces or campaigns show it at its best.
+ Counter-exemplars are as valuable as exemplars: "we would never ship this"
+ usually yields a sharper truth than an hour of affirmative description.
+2. **Work the material** - ask for what the human can show: screenshots,
+ links, exemplar products, brand docs, past campaigns, a rejected design and
+ why it was rejected. Every artifact is testimony to curate, never truth by
+ itself.
+3. **Draft** - write the smallest useful node prose from what the human said
+ or showed, asking of each truth why, with what, and how it is assembled.
+4. **Curate** - have the human keep, soften, reject, restate at a different
+ altitude, or record important claims before treating them as durable.
5. **Validate** - run Ghost validation and use Git review as the approval
boundary.
```bash
-ghost scan --format json
-ghost signals .
-ghost lint .ghost
-ghost verify .ghost --root .
+ghost validate
```
-Raw repo signals are source evidence only. They can support curated inventory,
-but they do not establish surface-composition guidance by themselves. Signal
-frequency may seed a draft, but it does not decide what the surface should do.
+Artifacts and brand docs are testimony only. They can seed a draft, but they do
+not establish brand truth by themselves — the human ratifies what is
+intentional.
-
+
-Keep each claim in the file that will make it useful later:
+The fingerprint is a **flat set of prose nodes** — no hierarchy, no inheritance,
+no edges. A node's identity is its file path with `.md` dropped
+(`marketing.email.md` is the node `marketing.email`). Its **kind** is the
+filename's first dotted segment (`principle.trust.md` → kind `principle`), and
+must be a kind declared in `glossary.md`; a bare name (`voice.md`) has
+no kind. Folders are a human-browsing convenience only.
-| Facet | What belongs there |
+Reserved at the package root are `manifest.yml`, `glossary.md`, and the
+`checks/` directory; every other `*.md` is a node. Moving or renaming a node
+changes its id.
+
+Node frontmatter carries only descriptive properties:
+
+| Property | What it does |
| --- | --- |
-| `intent.yml` | Audience, goals, anti-goals, situations, principles, and experience contracts. |
-| `inventory.yml` | Scopes, surface types, files, routes, libraries, assets, building blocks, exemplars, and source links. |
-| `composition.yml` | Repeatable rules, layouts, structures, flows, states, content patterns, behavior, and visual arrangements. |
-| `validate.yml` | Deterministic gates that can be checked from a diff. |
+| `description` | A short summary of the node — the retrieval payload the menu shows. |
+| `materials` | Optional list of locators for the concrete materials the truth is about: repo-relative paths/globs or absolute HTTPS URLs. |
+| _passthrough_ | Free-form keys (`audience`, `stage`, …) are preserved for host tooling. |
+
+`materials` is the single locator field. Components, patterns, logos, motion
+files, illustrations, and external asset libraries all use it. Guidance stays in
+prose; `materials` only says where the material is. Repo-relative locators also
+power `ghost review`: a diff that touches a matched path surfaces the node and
+its checks — see [Checks And Review](/docs/checks-and-review).
+
+The **glossary** (`glossary.md`) declares the kind vocabulary and what each
+kind means — its normative weight, so an agent knows how to read a node of
+that kind. Kinds are the author's choice; Ghost ships no fixed vocabulary.
-
+
-A useful fingerprint should help future agents choose, restrain, route, anchor,
-and review. It should not only describe what exists or collect every available
-style detail.
+State each truth at the altitude it is actually true. A universal truth is stated
+plainly. A narrower truth names its **condition** in the prose — the *situation*
+it applies in ("when a surface must show many items at once, carry hierarchy with
+weight, not color"), never a destination or filing bucket ("for dashboards:").
+The agent reads the condition and decides when the truth applies; that
+interpretation is the agent's job, not the filename's.
-Write facet content so generation decisions become explicit:
-
-- `goals` name what generated work should preserve.
-- `anti_goals` block plausible defaults that would make the surface feel
- generic or wrong.
-- `tradeoffs` say which value wins when choices conflict.
-- `situations` route guidance by task, surface type, state, or audience need.
-- `principles` capture broad product intent.
-- `experience_contracts` turn taste, trust, recovery, or disclosure into
- obligations.
-- `composition.patterns` give repeatable layout, flow, state, content,
- behavior, or visual rules.
-- `inventory.exemplars` anchor the guidance in concrete material an agent can
- inspect.
+
-Write less like a brand book and more like a decision engine.
+
+
+While drafting a node's prose body, ask three questions of every truth: *why*
+(the stance — audience, goals, anti-goals, principles), *with what* (the
+materials — building blocks, files, routes, assets, exemplars, source links),
+and *how it is assembled* (the patterns — layouts, flows, states, content
+patterns, behavior, visual arrangements). These are drafting prompts, never
+frontmatter fields, and a node may answer only one. Each answer lands as a
+steering dimension: stance prose is read first — weakest at moving output form
+on its own, but the yardstick every later selection is measured against; materials
+make a node concrete and drive pull ordering and review matching; patterns and
+Skeletons steer strongest, and Skeleton fences are extracted and emitted dead
+last so generation starts from them.
-
+
-Nested fingerprints are opt-in. Create a local `.ghost/` only when a surface
-should be assessed differently from the root product fingerprint.
+A useful fingerprint should help future agents choose, restrain, anchor, and
+manifest. It should not only describe what exists or collect every available
+style detail.
-Use a nested package when a surface has distinct users, information density,
-trust or recovery posture, interaction rhythm, component grammar, UI library
-usage, or review criteria for the same UI decision.
+Write node prose so generation decisions become explicit:
-Keep broad product-family guidance at the root. Put local obligations in the
-nearest package that owns the surface.
+- Name what generated work should preserve.
+- Block plausible defaults that would make the output feel generic or wrong.
+- Say which value wins when choices conflict.
+- State the condition under which a narrower truth applies.
+- Capture broad brand intent.
+- Turn taste, trust, recovery, or disclosure into obligations.
+- Give repeatable layout, flow, state, content, behavior, or visual rules.
+- Anchor the guidance in concrete exemplars an agent can inspect.
-```bash
-ghost init --scope apps/checkout
-ghost stack apps/checkout
-ghost lint --all
-ghost verify --all
-```
+Write less like a brand book and more like a decision engine.
-
+
+
+One contract per package: a repo's `.ghost/` is the whole fingerprint. Reach for
+a second package only when a distinct product owns its own fingerprint — a
+separate app in a monorepo — and target it with `ghost --package `.
-Uncommitted or unmerged fingerprint edits are drafts. Checked-in
-Ghost package facet files are canonical.
+
+
+
-Add deterministic checks sparingly, and only when a rule can be enforced
-deterministically.
+Uncommitted or unmerged fingerprint edits are drafts. Checked-in Ghost node prose
+is canonical.
diff --git a/apps/docs/src/content/docs/getting-started.mdx b/apps/docs/src/content/docs/getting-started.mdx
index ebc88699..023f0217 100644
--- a/apps/docs/src/content/docs/getting-started.mdx
+++ b/apps/docs/src/content/docs/getting-started.mdx
@@ -1,59 +1,56 @@
---
title: Getting Started
-description: Install Ghost, author a product-surface composition fingerprint, and use it to generate, validate, compare, and govern product surfaces.
+description: Install Ghost, author a brand fingerprint as a flat set of prose truths, and gather the right context before your agent builds.
kicker: Docs
section: guide
order: 10
slug: getting-started
---
-
+
-Ghost captures the composition of a product surface: the intent behind it, the
-materials it draws from, and the patterns that make it feel intentional. The
-public package is `@anarchitecture/ghost`, and it installs one CLI: `ghost`.
+
-The canonical portable fingerprint is a folder:
+Start with [Five-Minute Ghost](/docs/quickstart) — one file, one repeated
+decision, no vocabulary. This guide is the full loop for when you're ready to
+author a glossary and gather context end to end.
-```text
-.ghost/
- manifest.yml
- intent.yml
- inventory.yml
- composition.yml
- validate.yml
-```
-
-Generation starts from `intent.yml`, `inventory.yml`, and `composition.yml`.
-`validate.yml` checks validate the result afterward; they are not generation input.
-
-Nested product areas can add child package roots such as
-`apps/checkout/.ghost/`. Ghost resolves fingerprint stacks
-root-to-leaf for the file or diff being reviewed.
-
-
+
-
+Ghost is your brand as a **portable steering packet**: its truths, its stance,
+its conditions, written as prose your agent reads **before** it makes anything.
+A truth is stated once, at the altitude it is actually true, and the agent
+manifests it into whatever it is making — a screen, an email, a landing page, a
+sentence. The packet is agent-agnostic and travels with the repo. The public
+package is `@design-intelligence/ghost`, and it installs one CLI: `ghost`.
-
+The core loop is **feed-forward**: it grounds generation before your agent
+builds. An optional feed-back side — the checks directory and `ghost review` —
+assembles advisory review packets after, but never grades output itself. You
+work through a short loop:
-Ghost is pre-1.0 and under active development. The CLI, fingerprint schema,
-on-disk `.ghost/` package shape, and public JavaScript exports may
-change in breaking ways before a stable 1.0 release.
+```bash
+ghost init # scaffold .ghost/ with the steering starter
+ghost validate # artifact shape + node validity + kind-prefix check
+ghost gather # emit the menu for this task; your agent selects the truths it needs
+ghost pull # read the selected truths' full bodies
+ghost pulse # inspect the local signal while tuning the fingerprint
+```
-Breaking changes may ship in minor versions while Ghost is pre-1.0. Patch
-versions are reserved for fixes that should not require migration. If you adopt
-Ghost today, expect some churn, pin the version you depend on, and review
-release notes before upgrading.
+When you want review assertions too, opt in with `ghost checks init` and
+run `ghost review` against a diff — [Checks And Review](/docs/checks-and-review)
+covers that side of the loop.
-
+The CLI does the deterministic work; your agent does the interpretation. The
+rest of this guide walks the loop; the **How It Works** section at the end
+covers the data model underneath it.
```bash
-npm install -D @anarchitecture/ghost
+npm install -D @design-intelligence/ghost
npx ghost --help
npx ghost --help --all
npx ghost skill install
@@ -67,103 +64,148 @@ Once the skill is installed, ask your agent in plain English:
```text
Set up the Ghost fingerprint for this repo.
Brief this work from the Ghost fingerprint.
-Review this PR against the Ghost fingerprint.
```
-The skill tells the agent what to read, what to write, and which CLI checks to
-run.
+The skill tells the agent what to read, what to write, and how to gather.
-The CLI handles the deterministic package work. Your agent handles the
-composition work: interviewing, reading repo evidence, drafting facet edits, and
-asking you to curate the claims.
+Start with one truth, not the whole brand. The hardest part is picking the first
+one, so pick the decision whose review feedback you keep repeating — the checkout
+you always flag for trust, the voice you always re-tone, the empty state you
+always rewrite. Write that one down as a node, and grow the fingerprint decision
+by decision. One high-confidence truth beats an empty catalog.
+
+The CLI handles the deterministic package work. Your agent handles the authoring:
+interviewing you, working with the material you show it, drafting node prose,
+and asking you to curate the claims. `ghost init` scaffolds the steering starter: `manifest.yml`, a starter `glossary.md`, a core `index.md` node, and demo nodes for stance, composition, anti-goals, patterns, exemplars, materials, and decisions. The demo content is there for inspiration and guidance. Replace its claims, paths, examples, and decisions with your real product truth before using it to steer generation. Use `ghost init --template minimal` when you only want the small manifest/glossary/index starter.
```bash
ghost init
-ghost scan --format json
-ghost signals .
-ghost lint .ghost
-ghost verify .ghost --root .
+ghost validate
```
-The fingerprint records durable surface-composition guidance:
-
-1. **Intent** - what must remain true: what product this is, who it
- serves, which situations matter, and which principles or contracts apply.
-2. **Inventory** - the materials it draws from: topology, building blocks,
- files, routes, assets, libraries, exemplars, and source links agents may
- inspect or use.
-3. **Composition** - the patterns that make it intentional: rules, layouts,
- structures, flows, states, content, behavior, and visual arrangements.
-
-Raw repo signals are optional authoring evidence. Curate durable intent,
-inventory, and composition into the facet files, then use normal Git
-review for approval. For a fuller human-agent workflow, read
+Each node's prose records a durable brand truth. While drafting, ask three
+questions of every truth: **why** (the stance — what this brand is, who it
+serves, what must remain true), **with what** (the materials — building blocks,
+files, assets, and source links agents may inspect or use), and **how it is
+assembled** (the patterns that make it intentional — hierarchy, density,
+restraint, repetition, trust, and flow).
+
+These are drafting prompts, never frontmatter fields. Node frontmatter carries
+only `description` plus free-form passthrough
+keys. A node's **kind** comes from its filename prefix (`principle.density.md` →
+kind `principle`), and the kinds are declared in `glossary.md`. When a truth is
+narrower than universal, state its **condition** in the prose — the situation it
+applies in, never a filename destination. For a fuller human-agent workflow, read
[Fingerprint Authoring](/docs/fingerprint-authoring).
-
+
-Before generating or revising UI, gather Relay JSON for the target path:
+Before generating, gather the fingerprint menu. `gather` does no selection — it
+emits every node's id, kind, and description, and your agent reads the ask
+against it and pulls the truths it judges relevant:
```bash
-ghost relay gather apps/checkout/review/page.tsx --format json
+ghost gather "checkout settings"
+ghost gather "checkout settings" --format json
+ghost pull principle.trust voice # read the selected truths' full bodies
```
-`ghost.relay.gather/v2` is the agent contract. Agents should read `context`,
-`selected_context`, `targetPaths`, `source`, `stackDirs`, gaps, and trace fields
-from JSON. Plain `ghost relay gather ` remains a compact human preview.
-For prompt-shaped work where there is no clear path, host agents can create a
-`ghost.relay-request/v1` and run
-`ghost relay gather --request-stdin --format json`.
-Relay config controls the runtime. Omitted `base` uses the resolved fingerprint
-stack; `base.kind: none` lets frameworks provide declared request context
-without a `.ghost` package:
+This is "the right context at the right time": the agent selects just-in-time
+against the actual task, then manifests the truths into whatever it is building.
+Run it before generation, so the agent builds with the brand in hand rather than
+discovering the gaps in review.
+
+Each `gather` and `pull` also appends a structured JSONL event to
+`.ghost/.events`, a gitignored events tape. That one is for you: tweak a node's
+description, re-run the task, and `ghost pulse` shows whether the truth appeared
+on the menu, got pulled, was missed by name, or stayed cold.
```bash
-GHOST_RELAY_CONFIG=.agents/ghost/relay.yml ghost relay gather --request-stdin --format json
-ghost relay gather stacks/portal.renewal-reminder.email.yml --config .agents/ghost/relay.yml --format json
+ghost pulse
```
-The package remains the approved product-surface context; review and check
-commands apply it after implementation.
+Read `pulse` as a tuning signal, not a score. A cold node may need a sharper
+`description`; a repeated miss may mean the id or glossary vocabulary is not
+what agents expect; an abandoned gather means the menu did not lead to a useful
+selection. The events tape is local tuning signal, not canonical fingerprint state.
-
+
-After implementation, run Ghost against the same fingerprint:
+`ghost validate` checks the package deterministically: artifact shape, per-node
+validity, and that each node's kind prefix is declared in the glossary.
```bash
-ghost check --base main
-ghost review --base main
+ghost validate
+ghost validate --format json
```
-`ghost check` applies active deterministic gates from the resolved fingerprint
-stack for each changed file. `ghost review` emits advisory context grounded in
-the same selected context as Relay, selected validation checks, and the diff.
-
-Wrappers should consume `ghost check --format json` and map Ghost severities
-outside Ghost. Ghost severities remain `critical`, `serious`, and `nit`.
+An undeclared kind prefix is a warning with a "did you mean" suggestion, not a
+failure. Treat fingerprint edits as ordinary Git-reviewed changes: uncommitted
+edits are drafts; checked-in nodes are canonical.
-
+
-```bash
-ghost compare market/.ghost dashboard/.ghost
-ghost stack apps/checkout/review/page.tsx
-ghost ack --stance aligned --reason "Initial baseline"
-ghost track new-tracked.fingerprint.md
-ghost diverge typography --reason "Editorial product uses a different type scale"
+You can run the whole loop above without this section — your agent handles the
+format. Read on when you want to understand how the folder of files is wired.
+
+The fingerprint is a **flat set of prose nodes**:
+
+```text
+.ghost/
+ manifest.yml # schema + package id
+ glossary.md # the kind vocabulary + what each kind means
+ index.md # the curated front door node
+ principle.trust.md # a brand truth of kind `principle`
+ condition.density.md # a brand truth of kind `condition`
+ asset.logo.md # a truth that may point at concrete materials
+ voice.md # a brand truth without a kind
+ checks/ # optional review assertions; never a node
```
-Package comparison uses canonical `.ghost/` packages. `ack`,
-`track`, and `diverge` record stance for compatibility drift workflows that
-track direct fingerprint markdown references.
+There is no hierarchy, no inheritance, and no edges between nodes. A node's
+identity is its file path with `.md` dropped (`marketing.email.md` is the node
+`marketing.email`). Its **kind** is the filename's first dotted segment, and must
+be a kind declared in `glossary.md`; a bare name (`voice.md`) has
+no kind. Folders are a human-browsing convenience only — the model reads a
+flat menu.
+
+Altitude lives in the prose: a universal truth is stated plainly; a narrower
+truth names the *situation* it applies in. The agent reads the condition and
+decides when the truth applies. Reserved at the package root: `manifest.yml`,
+`glossary.md`, and `checks/`; every other `*.md` is a node.
+
+A node may carry `materials`: repo-relative paths/globs or HTTPS
+URLs for the concrete materials the prose governs. `ghost gather` emits the menu;
+the agent selects and manifests. Checks are used by `ghost review`, never by
+generation.
+
+One contract per package: a repo's `.ghost/` is the whole fingerprint.
+
+
+
+
+
+
+
+Ghost is pre-1.0 and under active development. The CLI, fingerprint schema,
+on-disk `.ghost/` package shape, and public JavaScript exports may
+change in breaking ways before a stable 1.0 release.
+
+Breaking changes may ship in minor versions while Ghost is pre-1.0. Patch
+versions are reserved for fixes that should not require migration. If you adopt
+Ghost today, expect some churn, pin the version you depend on, and review
+release notes before upgrading.
+
+
diff --git a/apps/docs/src/content/docs/quickstart.mdx b/apps/docs/src/content/docs/quickstart.mdx
new file mode 100644
index 00000000..15843a63
--- /dev/null
+++ b/apps/docs/src/content/docs/quickstart.mdx
@@ -0,0 +1,102 @@
+---
+title: Five-Minute Ghost
+description: Start your brand's steering packet with the one decision you keep repeating, so your agent reads it before it builds.
+kicker: Docs
+section: guide
+order: 5
+slug: quickstart
+---
+
+
+
+You keep leaving the same review comment.
+
+"Don't introduce a new button style on checkout." "This empty state needs a way
+out, not just a message." "Slow down — this form is doing three things at once."
+
+Your agent can build the screen. What it can't do is remember the decision you'd
+make when it does. So you catch the same drift in review, every time, on every
+surface.
+
+Ghost is one move against that: **write the decision down once, in your repo, and
+your agent reads it before it builds** — not after you flag it.
+
+
+
+
+
+Install Ghost and teach your agent to use it:
+
+```bash
+npm install -D @design-intelligence/ghost
+npx ghost skill install
+```
+
+Then ask your agent, in plain English:
+
+```text
+Set up Ghost for this repo, starting with the checkout surface.
+```
+
+That's it. Your agent scaffolds a small `.ghost/` folder and writes your first
+file. You don't need to learn the format to start — you just need to know which
+decision you keep repeating.
+
+
+
+
+
+Pick the one decision whose feedback you repeat most. Say it's about trust at
+checkout. Your agent writes that as a single small file,
+`.ghost/principle.trust.md` — the filename's first segment (`principle`) is its
+*kind*, declared in the package's `glossary.md`:
+
+```markdown
+---
+description: Trust at the payment moment.
+---
+
+Near the moment of payment, reduce felt risk. Keep reassurance close to the
+action — proximity beats completeness. Never introduce a new visual system here.
+```
+
+A few lines of plain prose, checked into your repo next to the code. That's the
+whole artifact. No schema to memorize, no config to wire up. You wrote the
+comment you were going to leave anyway — once, in a place your agent can read.
+
+
+
+
+
+Now the loop runs the other direction. Before your agent builds on checkout, it
+reads what you wrote:
+
+```text
+Brief this checkout change from Ghost first.
+```
+
+The comment you used to leave in review is now context the agent has up front.
+That's the entire idea: **give the agent the decision before it builds, not
+after.**
+
+And notice what you actually made. That file isn't a note about one screen —
+it's the first truth in a **portable steering packet**. The same
+`principle.trust.md` steers the confirmation email, the receipt page, the
+error message when the card declines. Any agent reads it — Claude Code, Codex,
+Cursor, Goose — and it travels with every clone and fork of the repo.
+
+
+
+
+
+Don't fingerprint the whole brand. One good file beats an empty catalog. The
+next time you catch yourself repeating a decision — the settings form you always
+re-pace, the voice you always re-tone — write that one down too. The fingerprint
+grows decision by decision.
+
+When you're ready for the full workflow — the glossary, gathering context,
+validating — read [Getting Started](/docs/getting-started). When you want to know
+how the folder of files works underneath, that guide's **How It Works** section
+has it. You don't need either to get your first win.
+
+
diff --git a/apps/docs/src/contexts/ThemePanelContext.tsx b/apps/docs/src/contexts/ThemePanelContext.tsx
deleted file mode 100644
index 0b01302f..00000000
--- a/apps/docs/src/contexts/ThemePanelContext.tsx
+++ /dev/null
@@ -1,248 +0,0 @@
-"use client";
-
-import {
- ALL_THEMEABLE_KEYS,
- DEFAULT_COLORS_DARK,
- DEFAULT_COLORS_LIGHT,
- DEFAULT_SHADOWS_DARK,
- DEFAULT_SHADOWS_LIGHT,
- generateCSSExport,
- PRESETS,
- scaleRadius,
- scaleShadows,
- type ThemePreset,
- useTheme as useNextTheme,
-} from "ghost-ui";
-import type React from "react";
-import {
- createContext,
- useCallback,
- useContext,
- useEffect,
- useRef,
- useState,
-} from "react";
-
-const STORAGE_KEY = "ghost-ui-theme";
-
-interface StoredTheme {
- presetId: string | null;
- overrides: Record;
- radiusScale: number;
- shadowScale: number;
-}
-
-interface ThemePanelContextType {
- isOpen: boolean;
- setOpen: (open: boolean) => void;
- activePresetId: string | null;
- overrides: Record;
- radiusScale: number;
- shadowScale: number;
- applyPreset: (id: string) => void;
- setVariable: (key: string, value: string) => void;
- setVariables: (vars: Record) => void;
- setRadiusScale: (factor: number) => void;
- setShadowScale: (factor: number) => void;
- reset: () => void;
- exportCSS: () => string;
-}
-
-const ThemePanelContext = createContext(
- undefined,
-);
-
-export function ThemePanelProvider({
- children,
-}: {
- children: React.ReactNode;
-}) {
- const { resolvedTheme } = useNextTheme();
- const isDark = resolvedTheme === "dark";
-
- const [isOpen, setOpen] = useState(false);
- const [activePresetId, setActivePresetId] = useState(
- "default",
- );
- const [overrides, setOverrides] = useState>({});
- const [radiusScale, setRadiusScaleState] = useState(50);
- const [shadowScale, setShadowScaleState] = useState(50);
- const [mounted, setMounted] = useState(false);
- const saveTimeoutRef = useRef>(undefined);
- const prevKeysRef = useRef>(new Set());
-
- // Load from localStorage on mount
- useEffect(() => {
- try {
- const stored = localStorage.getItem(STORAGE_KEY);
- if (stored) {
- const data: StoredTheme = JSON.parse(stored);
- setActivePresetId(data.presetId);
- setOverrides(data.overrides);
- setRadiusScaleState(data.radiusScale ?? 50);
- setShadowScaleState(data.shadowScale ?? 50);
- }
- } catch {
- // ignore corrupted data
- }
- setMounted(true);
- }, []);
-
- // Apply overrides to document whenever they change
- useEffect(() => {
- if (!mounted) return;
-
- const currentKeys = new Set(Object.keys(overrides));
-
- // Remove properties that were in previous overrides but not in current
- for (const key of prevKeysRef.current) {
- if (!currentKeys.has(key)) {
- document.documentElement.style.removeProperty(key);
- }
- }
-
- // Apply current overrides
- requestAnimationFrame(() => {
- for (const [key, value] of Object.entries(overrides)) {
- document.documentElement.style.setProperty(key, value);
- }
- });
-
- prevKeysRef.current = currentKeys;
- }, [overrides, mounted]);
-
- // Debounced save to localStorage
- useEffect(() => {
- if (!mounted) return;
-
- if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
- saveTimeoutRef.current = setTimeout(() => {
- const data: StoredTheme = {
- presetId: activePresetId,
- overrides,
- radiusScale,
- shadowScale,
- };
- localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
- }, 300);
-
- return () => {
- if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
- };
- }, [overrides, activePresetId, radiusScale, shadowScale, mounted]);
-
- // Re-apply preset when dark/light mode toggles
- useEffect(() => {
- if (!mounted) return;
- if (!activePresetId || activePresetId === "default") return;
-
- const preset = PRESETS.find((p) => p.id === activePresetId);
- if (!preset) return;
-
- const modeVars = isDark ? preset.variables.dark : preset.variables.light;
- if (Object.keys(modeVars).length > 0) {
- setOverrides(modeVars);
- }
- }, [isDark, mounted]); // eslint-disable-line react-hooks/exhaustive-deps
-
- const applyPreset = useCallback(
- (id: string) => {
- setActivePresetId(id);
-
- if (id === "default") {
- // Clear all overrides to restore CSS defaults
- setOverrides({});
- setRadiusScaleState(50);
- setShadowScaleState(50);
- return;
- }
-
- const preset = PRESETS.find((p) => p.id === id);
- if (!preset) return;
-
- const modeVars = isDark ? preset.variables.dark : preset.variables.light;
- setOverrides(modeVars);
- setRadiusScaleState(50);
- setShadowScaleState(50);
- },
- [isDark],
- );
-
- const setVariable = useCallback((key: string, value: string) => {
- setActivePresetId(null); // mark as custom
- setOverrides((prev) => ({ ...prev, [key]: value }));
- }, []);
-
- const setVariables = useCallback((vars: Record) => {
- setActivePresetId(null);
- setOverrides((prev) => ({ ...prev, ...vars }));
- }, []);
-
- const setRadiusScale = useCallback((factor: number) => {
- setRadiusScaleState(factor);
- setActivePresetId(null);
- const radiusVars = scaleRadius(factor);
- setOverrides((prev) => ({ ...prev, ...radiusVars }));
- }, []);
-
- const setShadowScale = useCallback(
- (factor: number) => {
- setShadowScaleState(factor);
- setActivePresetId(null);
- const shadowVars = scaleShadows(factor, isDark);
- setOverrides((prev) => ({ ...prev, ...shadowVars }));
- },
- [isDark],
- );
-
- const reset = useCallback(() => {
- // Remove all overrides from document
- for (const key of ALL_THEMEABLE_KEYS) {
- document.documentElement.style.removeProperty(key);
- }
- // Also remove any current override keys not in ALL_THEMEABLE_KEYS
- for (const key of Object.keys(overrides)) {
- document.documentElement.style.removeProperty(key);
- }
-
- setOverrides({});
- setActivePresetId("default");
- setRadiusScaleState(50);
- setShadowScaleState(50);
- localStorage.removeItem(STORAGE_KEY);
- }, [overrides]);
-
- const exportCSS = useCallback(() => {
- return generateCSSExport(overrides);
- }, [overrides]);
-
- return (
-
- {children}
-
- );
-}
-
-export function useThemePanel() {
- const context = useContext(ThemePanelContext);
- if (context === undefined) {
- throw new Error("useThemePanel must be used within a ThemePanelProvider");
- }
- return context;
-}
diff --git a/apps/docs/src/generated/cli-manifest.json b/apps/docs/src/generated/cli-manifest.json
index 4d5b9a37..02c31fa7 100644
--- a/apps/docs/src/generated/cli-manifest.json
+++ b/apps/docs/src/generated/cli-manifest.json
@@ -1,19 +1,27 @@
{
- "generatedAt": "2026-06-24T16:55:39.949Z",
+ "generatedAt": "2026-07-07T16:48:56.039Z",
"tools": [
{
"tool": "ghost",
"commands": [
{
"tool": "ghost",
- "name": "lint",
- "rawName": "lint [file]",
- "description": "Validate a root Ghost fingerprint package, split fingerprint artifacts, checks, or direct markdown — defaults to .ghost",
+ "name": "validate",
+ "rawName": "validate [file]",
+ "description": "Validate the Ghost fingerprint package — manifest shape, node validity, material locators, check references, and glossary kind prefixes. Defaults to .ghost.",
"group": "core",
"defaultHelp": true,
- "compactName": "lint",
- "summary": "Validate a fingerprint package or artifact.",
+ "compactName": "validate",
+ "summary": "Validate the fingerprint: artifact shape + node validity.",
"options": [
+ {
+ "rawName": "--package ",
+ "name": "package",
+ "description": "Use this fingerprint package directory (default: ./.ghost)",
+ "default": null,
+ "takesValue": true,
+ "negated": false
+ },
{
"rawName": "--format ",
"name": "format",
@@ -21,14 +29,6 @@
"default": "cli",
"takesValue": true,
"negated": false
- },
- {
- "rawName": "--all",
- "name": "all",
- "description": "Validate every nested fingerprint package and its resolved fingerprint stack",
- "default": null,
- "takesValue": false,
- "negated": false
}
]
},
@@ -36,20 +36,12 @@
"tool": "ghost",
"name": "init",
"rawName": "init",
- "description": "Create a root .ghost split fingerprint package",
+ "description": "Create a root .ghost node fingerprint package",
"group": "core",
"defaultHelp": true,
"compactName": "init",
- "summary": "Create .ghost/ package facets.",
+ "summary": "Scaffold a .ghost/ fingerprint: manifest, glossary, and a node.",
"options": [
- {
- "rawName": "--scope ",
- "name": "scope",
- "description": "Create a scoped /.ghost fingerprint package",
- "default": null,
- "takesValue": true,
- "negated": false
- },
{
"rawName": "--package ",
"name": "package",
@@ -59,27 +51,19 @@
"negated": false
},
{
- "rawName": "--reference ",
- "name": "reference",
- "description": "Reference UI registry, library path, or fingerprint to record in inventory building blocks",
+ "rawName": "--template ",
+ "name": "template",
+ "description": "Init template to scaffold (default: steering)",
"default": null,
"takesValue": true,
"negated": false
},
{
- "rawName": "--monorepo",
- "name": "monorepo",
- "description": "Detect monorepo child package roots and propose scoped Ghost packages",
+ "rawName": "--with ",
+ "name": "with",
+ "description": "Comma-separated capabilities to add after scaffolding (e.g. checks)",
"default": null,
- "takesValue": false,
- "negated": false
- },
- {
- "rawName": "--apply",
- "name": "apply",
- "description": "With --monorepo, create detected child scoped packages",
- "default": null,
- "takesValue": false,
+ "takesValue": true,
"negated": false
},
{
@@ -102,18 +86,18 @@
},
{
"tool": "ghost",
- "name": "verify",
- "rawName": "verify [dir]",
- "description": "Verify a root Ghost fingerprint package: intent/composition evidence, inventory exemplars, and checks are grounded.",
+ "name": "gather",
+ "rawName": "gather [...ask]",
+ "description": "Emit the fingerprint menu — every node's id, kind, and description — for the agent to select from.",
"group": "core",
"defaultHelp": true,
- "compactName": "verify",
- "summary": "Verify evidence, exemplar paths, and typed refs.",
+ "compactName": "gather [ask]",
+ "summary": "Emit the fingerprint menu for the agent to select from.",
"options": [
{
- "rawName": "--root ",
- "name": "root",
- "description": "Optional target root used to resolve fingerprint evidence and exemplar paths (default: cwd)",
+ "rawName": "--package ",
+ "name": "package",
+ "description": "Use this fingerprint package directory (default: ./.ghost)",
"default": null,
"takesValue": true,
"negated": false
@@ -121,15 +105,15 @@
{
"rawName": "--format ",
"name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
+ "description": "Output format: markdown or json",
+ "default": "markdown",
"takesValue": true,
"negated": false
},
{
- "rawName": "--all",
- "name": "all",
- "description": "Verify every nested fingerprint package and its resolved fingerprint stack",
+ "rawName": "--wild",
+ "name": "wild",
+ "description": "Include wild-posture nodes in the menu",
"default": null,
"takesValue": false,
"negated": false
@@ -138,253 +122,114 @@
},
{
"tool": "ghost",
- "name": "scan",
- "rawName": "scan [dir]",
- "description": "Report sparse fingerprint package contribution facets: intent, inventory, composition, validate, and the next BYOA step.",
+ "name": "pull",
+ "rawName": "pull <...ids>",
+ "description": "Emit the named nodes' full prose bodies, and append the pull to the events tape.",
"group": "core",
"defaultHelp": true,
- "compactName": "scan",
- "summary": "Report fingerprint contribution facets.",
+ "compactName": "pull",
+ "summary": "Emit the named nodes' bodies; log the pull to .ghost/.events.",
"options": [
{
- "rawName": "--include-scopes",
- "name": "includeScopes",
- "description": "Also report per-scope survey and fingerprint artifacts under modules// and fingerprints/.md",
- "default": null,
- "takesValue": false,
- "negated": false
- },
- {
- "rawName": "--include-nested",
- "name": "includeNested",
- "description": "Also list nested fingerprint packages and contribution state",
+ "rawName": "--package ",
+ "name": "package",
+ "description": "Use this fingerprint package directory (default: ./.ghost)",
"default": null,
- "takesValue": false,
- "negated": false
- },
- {
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
- "takesValue": true,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost",
- "name": "stack",
- "rawName": "stack [paths...]",
- "description": "Inspect the nested Ghost fingerprint stack for one or more repo paths.",
- "group": "advanced",
- "defaultHelp": false,
- "compactName": "stack",
- "summary": "Inspect a nested fingerprint stack for repo paths.",
- "options": [
- {
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
- "takesValue": true,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost",
- "name": "signals",
- "rawName": "signals [path]",
- "description": "Emit deterministic raw repo signals as JSON: package manifests, language histogram, candidate config files, registry presence, top-level tree, and git remote.",
- "group": "advanced",
- "defaultHelp": false,
- "compactName": "signals",
- "summary": "Emit raw repo signals for fingerprint authoring.",
- "options": []
- },
- {
- "tool": "ghost",
- "name": "describe",
- "rawName": "describe ",
- "description": "Print a section map of a markdown file (line ranges + token estimates).",
- "group": "advanced",
- "defaultHelp": false,
- "compactName": "describe",
- "summary": "Print markdown section ranges.",
- "options": [
- {
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
"takesValue": true,
"negated": false
- }
- ]
- },
- {
- "tool": "ghost",
- "name": "diff",
- "rawName": "diff ",
- "description": "Direct markdown diff between two fingerprint.md files — what decisions, palette roles, and tokens changed (text-level, NOT embedding distance; for that, use `ghost compare`).",
- "group": "maintenance",
- "defaultHelp": false,
- "compactName": "diff",
- "summary": "Diff two direct markdown fingerprints.",
- "options": [
+ },
{
"rawName": "--format ",
"name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
- "takesValue": true,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost",
- "name": "survey",
- "rawName": "survey [...surveys]",
- "description": "Survey/cache helpers for ghost.survey/v1 files. Ops: merge, fix-ids, summarize, catalog, patterns.",
- "group": "maintenance",
- "defaultHelp": false,
- "compactName": "survey",
- "summary": "Run legacy survey helpers.",
- "options": [
- {
- "rawName": "-o, --out ",
- "name": "out",
- "description": "Write the result to this path (default: stdout)",
- "default": null,
+ "description": "Output format: markdown or json",
+ "default": "markdown",
"takesValue": true,
"negated": false
},
{
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: summarize/catalog use markdown or json; patterns use yaml, json, or markdown",
- "default": null,
- "takesValue": true,
- "negated": false
+ "rawName": "--no-materials",
+ "name": "materials",
+ "description": "Emit material locators only; do not inline files",
+ "default": true,
+ "takesValue": false,
+ "negated": true
},
{
- "rawName": "--kind ",
- "name": "kind",
- "description": "survey catalog filter: include only this value kind",
- "default": null,
+ "rawName": "--order ",
+ "name": "order",
+ "description": "Output order: steering or given",
+ "default": "steering",
"takesValue": true,
"negated": false
},
{
- "rawName": "--budget ",
- "name": "budget",
- "description": "survey summarize budget: compact, standard, full",
- "default": "standard",
- "takesValue": true,
- "negated": false
+ "rawName": "--no-events",
+ "name": "events",
+ "description": "Skip appending to .ghost/.events",
+ "default": true,
+ "takesValue": false,
+ "negated": true
}
]
},
{
"tool": "ghost",
- "name": "emit",
- "rawName": "emit ",
- "description": "Emit a derived artifact from the fingerprint package (review-command).",
+ "name": "pulse",
+ "rawName": "pulse",
+ "description": "Summarize local gather/pull events from .ghost/.events.",
"group": "core",
"defaultHelp": true,
- "compactName": "emit",
- "summary": "Emit review-command artifacts.",
+ "compactName": "pulse",
+ "summary": "Summarize local gather/pull events from .ghost/.events.",
"options": [
- {
- "rawName": "--path ",
- "name": "path",
- "description": "Resolve a nested fingerprint stack for this repo path",
- "default": null,
- "takesValue": true,
- "negated": false
- },
{
"rawName": "--package ",
"name": "package",
- "description": "Use exactly this fingerprint package directory instead of resolving a stack",
+ "description": "Use this fingerprint package directory (default: ./.ghost)",
"default": null,
"takesValue": true,
"negated": false
},
{
- "rawName": "-o, --out ",
- "name": "out",
- "description": "Output path (review-command → .claude/commands/design-review.md)",
- "default": null,
+ "rawName": "--format ",
+ "name": "format",
+ "description": "Output format: markdown or json",
+ "default": "markdown",
"takesValue": true,
"negated": false
- },
- {
- "rawName": "--stdout",
- "name": "stdout",
- "description": "Write to stdout instead of a file",
- "default": null,
- "takesValue": false,
- "negated": false
}
]
},
{
"tool": "ghost",
- "name": "compare",
- "rawName": "compare [...fingerprints]",
- "description": "Compare two or more fingerprints or root .ghost bundles. N=2 returns a pairwise delta; N≥3 returns a composite fingerprint.",
- "group": "compare",
- "defaultHelp": false,
- "compactName": "compare",
- "summary": "Compare fingerprint packages.",
+ "name": "review",
+ "rawName": "review",
+ "description": "Emit an advisory review packet for a diff using material-backed nodes and checks.",
+ "group": "core",
+ "defaultHelp": true,
+ "compactName": "review",
+ "summary": "Emit an advisory review packet for a diff (needs .ghost/checks/).",
"options": [
{
- "rawName": "--semantic",
- "name": "semantic",
- "description": "Qualitative diff of decisions + palette (N=2 only)",
- "default": null,
- "takesValue": false,
- "negated": false
- },
- {
- "rawName": "--temporal",
- "name": "temporal",
- "description": "Add velocity, trajectory, and ack bounds (N=2, reads .ghost/history.jsonl)",
- "default": null,
- "takesValue": false,
- "negated": false
- },
- {
- "rawName": "--history-dir ",
- "name": "historyDir",
- "description": "Directory containing .ghost/history.jsonl (for --temporal, defaults to cwd)",
+ "rawName": "--package ",
+ "name": "package",
+ "description": "Use this fingerprint package directory (default: ./.ghost)",
"default": null,
"takesValue": true,
"negated": false
},
{
- "rawName": "--gate",
- "name": "gate",
- "description": "Reconcile against a sync manifest and emit a structured pass/fail verdict (N=2 only)",
- "default": null,
- "takesValue": false,
- "negated": false
- },
- {
- "rawName": "--sync ",
- "name": "sync",
- "description": "Sync manifest path for --gate (default: ./.ghost-sync.json)",
+ "rawName": "--base [",
+ "name": "base",
+ "description": "Git ref to diff against (default: HEAD)",
"default": null,
"takesValue": true,
"negated": false
},
{
- "rawName": "--max-divergence-days ]",
- "name": "maxDivergenceDays",
- "description": "For --gate: flag diverging dimensions older than this many days as uncovered",
+ "rawName": "--diff ",
+ "name": "diff",
+ "description": "Read diff from a file, or '-' for stdin",
"default": null,
"takesValue": true,
"negated": false
@@ -392,115 +237,67 @@
{
"rawName": "--format ",
"name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
- "takesValue": true,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost",
- "name": "ack",
- "rawName": "ack",
- "description": "Acknowledge current drift — record intentional stance toward the tracked fingerprint",
- "group": "compare",
- "defaultHelp": false,
- "compactName": "ack",
- "summary": "Record stance toward tracked drift.",
- "options": [
- {
- "rawName": "-c, --config ",
- "name": "config",
- "description": "Path to ghost config file",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "-d, --dimension ",
- "name": "dimension",
- "description": "Acknowledge a specific dimension only",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--stance ",
- "name": "stance",
- "description": "Stance: aligned, accepted, or diverging",
- "default": "accepted",
+ "description": "Output format: markdown or json",
+ "default": "markdown",
"takesValue": true,
"negated": false
},
{
- "rawName": "--reason ",
- "name": "reason",
- "description": "Reason for this acknowledgment",
+ "rawName": "--json",
+ "name": "json",
+ "description": "Emit the raw JSON packet",
"default": null,
- "takesValue": true,
+ "takesValue": false,
"negated": false
},
{
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
- "takesValue": true,
- "negated": false
+ "rawName": "--no-probes",
+ "name": "probes",
+ "description": "Skip check probe shell commands",
+ "default": true,
+ "takesValue": false,
+ "negated": true
}
]
},
{
"tool": "ghost",
- "name": "track",
- "rawName": "track ",
- "description": "Track another fingerprint as this repo's reference",
- "group": "compare",
- "defaultHelp": false,
- "compactName": "track",
- "summary": "Shift the tracked reference fingerprint.",
+ "name": "export",
+ "rawName": "export",
+ "description": "Package the fingerprint as a portable brand artifact with a locator audit.",
+ "group": "core",
+ "defaultHelp": true,
+ "compactName": "export",
+ "summary": "Package the fingerprint as a portable brand artifact.",
"options": [
{
- "rawName": "-d, --dimension ",
- "name": "dimension",
- "description": "Track only for a specific dimension",
+ "rawName": "--out ",
+ "name": "out",
+ "description": "Write the archive to this path",
"default": null,
"takesValue": true,
"negated": false
},
{
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
- "takesValue": true,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost",
- "name": "diverge",
- "rawName": "diverge ",
- "description": "Declare intentional divergence on a dimension",
- "group": "compare",
- "defaultHelp": false,
- "compactName": "diverge",
- "summary": "Declare intentional divergence on a dimension.",
- "options": [
+ "rawName": "--no-checks",
+ "name": "checks",
+ "description": "Exclude the checks/ directory from the archive",
+ "default": true,
+ "takesValue": false,
+ "negated": true
+ },
{
- "rawName": "-c, --config ",
- "name": "config",
- "description": "Path to ghost config file",
+ "rawName": "--strict",
+ "name": "strict",
+ "description": "Exit 2 if any referenced local material locators will not travel",
"default": null,
- "takesValue": true,
+ "takesValue": false,
"negated": false
},
{
- "rawName": "-r, --reason ",
- "name": "reason",
- "description": "Why this dimension is intentionally diverging",
+ "rawName": "--package ",
+ "name": "package",
+ "description": "Use this fingerprint package directory (default: ./.ghost)",
"default": null,
"takesValue": true,
"negated": false
@@ -508,8 +305,8 @@
{
"rawName": "--format ",
"name": "format",
- "description": "Output format: cli or json",
- "default": "cli",
+ "description": "Output format: markdown or json",
+ "default": "markdown",
"takesValue": true,
"negated": false
}
@@ -517,58 +314,18 @@
},
{
"tool": "ghost",
- "name": "drift",
- "rawName": "drift ",
- "description": "Inspect Ghost drift status or run the stance-ledger check.",
- "group": "compare",
- "defaultHelp": false,
- "compactName": "drift check",
- "summary": "Run the continuous design-loop drift check.",
+ "name": "checks",
+ "rawName": "checks ",
+ "description": "Manage review checks: init.",
+ "group": "core",
+ "defaultHelp": true,
+ "compactName": "checks init",
+ "summary": "Scaffold .ghost/checks/ with review assertions.",
"options": [
{
"rawName": "--package ",
"name": "package",
- "description": "Exact fingerprint package directory",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--config ",
- "name": "config",
- "description": "Path to ghost config file for tracked source",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--local ",
- "name": "local",
- "description": "Local fingerprint or bundle to check",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--tracked ",
- "name": "tracked",
- "description": "Tracked/reference fingerprint or bundle",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--sync ",
- "name": "sync",
- "description": "Sync manifest path (default: ./.ghost-sync.json)",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--max-divergence-days ",
- "name": "maxDivergenceDays",
- "description": "Flag diverging dimensions older than this many days as uncovered",
+ "description": "Use this fingerprint package directory (default: ./.ghost)",
"default": null,
"takesValue": true,
"negated": false
@@ -576,8 +333,8 @@
{
"rawName": "--format ",
"name": "format",
- "description": "Output format: markdown or json",
- "default": "markdown",
+ "description": "Output format: cli or json",
+ "default": "cli",
"takesValue": true,
"negated": false
}
@@ -585,67 +342,19 @@
},
{
"tool": "ghost",
- "name": "relay",
- "rawName": "relay [target]",
- "description": "Gather Relay context for an agent target.",
- "group": "core",
- "defaultHelp": true,
- "compactName": "relay gather",
- "summary": "Gather fingerprint context for an agent target.",
+ "name": "manifest",
+ "rawName": "manifest",
+ "description": "Emit a self-describing JSON manifest of every command and flag.",
+ "group": "advanced",
+ "defaultHelp": false,
+ "compactName": "manifest",
+ "summary": "Emit a self-describing JSON manifest of commands and flags.",
"options": [
- {
- "rawName": "--package ",
- "name": "package",
- "description": "Use exactly this fingerprint package directory instead of resolving a stack",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--name ",
- "name": "name",
- "description": "Override the gathered context name (default: intent.yml product or resolved scope)",
- "default": null,
- "takesValue": true,
- "negated": false
- },
{
"rawName": "--format ",
"name": "format",
- "description": "Output format: markdown or json",
- "default": "markdown",
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--config ",
- "name": "config",
- "description": "Load an explicit Ghost Relay config",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--request ",
- "name": "request",
- "description": "Load a structured Ghost Relay request",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--request-stdin",
- "name": "requestStdin",
- "description": "Read a structured Ghost Relay request from stdin",
- "default": null,
- "takesValue": false,
- "negated": false
- },
- {
- "rawName": "--mode ",
- "name": "mode",
- "description": "Relay mode: generation, review, or prompt",
- "default": "generation",
+ "description": "Output format: json",
+ "default": "json",
"takesValue": true,
"negated": false
}
@@ -672,7 +381,7 @@
{
"rawName": "--agent ",
"name": "agent",
- "description": "Agent destination to use when --dest is omitted: claude, cursor, codex, opencode",
+ "description": "Agent destination to use when --dest is omitted: claude, cursor, codex, opencode, goose",
"default": null,
"takesValue": true,
"negated": false
@@ -686,177 +395,6 @@
"negated": false
}
]
- },
- {
- "tool": "ghost",
- "name": "check",
- "rawName": "check",
- "description": "Run active ghost.validate/v1 gates from the resolved fingerprint stack against a git diff.",
- "group": "core",
- "defaultHelp": true,
- "compactName": "check",
- "summary": "Run active deterministic gates against a diff.",
- "options": [
- {
- "rawName": "--base [",
- "name": "base",
- "description": "Git ref to diff against (default: HEAD)",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--diff ]",
- "name": "diff",
- "description": "Unified diff file to check instead of running git diff. Use '-' for stdin.",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--package ",
- "name": "package",
- "description": "Exact fingerprint package directory; bypasses stack discovery",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: markdown or json",
- "default": "markdown",
- "takesValue": true,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost",
- "name": "review",
- "rawName": "review",
- "description": "Emit an evidence-routed advisory review prompt from the fingerprint package and a git diff.",
- "group": "core",
- "defaultHelp": true,
- "compactName": "review",
- "summary": "Emit an advisory packet from fingerprint facets and a diff.",
- "options": [
- {
- "rawName": "--base [",
- "name": "base",
- "description": "Git ref to diff against (default: HEAD)",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--diff ]",
- "name": "diff",
- "description": "Unified diff file to review instead of running git diff. Use '-' for stdin.",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--package ",
- "name": "package",
- "description": "Exact fingerprint package directory; bypasses stack discovery",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--max-diff-bytes ",
- "name": "maxDiffBytes",
- "description": "Maximum diff bytes to include in the review packet (default: 200000)",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--format ",
- "name": "format",
- "description": "Output format: markdown or json",
- "default": "markdown",
- "takesValue": true,
- "negated": false
- }
- ]
- }
- ],
- "globalOptions": [
- {
- "rawName": "-h, --help",
- "name": "help",
- "description": "Display this message",
- "default": null
- },
- {
- "rawName": "-v, --version",
- "name": "version",
- "description": "Display version number",
- "default": null
- }
- ]
- },
- {
- "tool": "ghost-fleet",
- "commands": [
- {
- "tool": "ghost-fleet",
- "name": "members",
- "rawName": "members [dir]",
- "description": "List registered fleet members and their freshness — one row per (map.md, fingerprint.md) subdirectory.",
- "options": [
- {
- "rawName": "--json",
- "name": "json",
- "description": "Output JSON (one object per member) instead of a table",
- "default": null,
- "takesValue": false,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost-fleet",
- "name": "view",
- "rawName": "view [dir]",
- "description": "Compute the fleet's pairwise distances, group-by tables, and tracks-graph; emit fleet.md + fleet.json into /reports/.",
- "options": [
- {
- "rawName": "--id ",
- "name": "id",
- "description": "Override the fleet id (default: directory basename slug)",
- "default": null,
- "takesValue": true,
- "negated": false
- },
- {
- "rawName": "--out ",
- "name": "out",
- "description": "Reports directory (default: /reports)",
- "default": null,
- "takesValue": true,
- "negated": false
- }
- ]
- },
- {
- "tool": "ghost-fleet",
- "name": "emit",
- "rawName": "emit ",
- "description": "Emit the ghost-fleet agentskills.io bundle (kind: skill).",
- "options": [
- {
- "rawName": "-o, --out ",
- "name": "out",
- "description": "Output directory (default: .claude/skills/ghost-fleet)",
- "default": null,
- "takesValue": true,
- "negated": false
- }
- ]
}
],
"globalOptions": [
diff --git a/apps/docs/src/lib/component-docs.ts b/apps/docs/src/lib/component-docs.ts
deleted file mode 100644
index 36f760b3..00000000
--- a/apps/docs/src/lib/component-docs.ts
+++ /dev/null
@@ -1,673 +0,0 @@
-export type PropDef = {
- name: string;
- type: string;
- default?: string;
- description: string;
-};
-
-export type ExampleMeta = {
- name: string;
- title: string;
- description?: string;
-};
-
-export type ComponentDoc = {
- description: string;
- usage: string;
- props: PropDef[];
- composedWith: string[];
- examples: ExampleMeta[];
-};
-
-const docs: Record = {
- message: {
- description:
- "Renders a single chat message with support for markdown streaming, actions, and branching.",
- usage: `import {
- Message,
- MessageContent,
- MessageResponse,
- MessageActions,
- MessageAction,
-} from "ghost-ui";
-
-
-
- Hello, how can I help?
-
-
-
-
-
-
- `,
- props: [
- {
- name: "from",
- type: '"user" | "assistant" | "system"',
- description:
- "The role of the message sender, controls alignment and styling.",
- },
- {
- name: "children",
- type: "ReactNode",
- description:
- "Message sub-components (MessageContent, MessageActions, etc.).",
- },
- ],
- composedWith: [
- "conversation",
- "prompt-input",
- "reasoning",
- "chain-of-thought",
- "code-block",
- ],
- examples: [
- {
- name: "with-actions",
- title: "With Actions",
- description: "Message with copy and regenerate action buttons.",
- },
- {
- name: "streaming",
- title: "Streaming Response",
- description: "Message with an animated streaming indicator.",
- },
- ],
- },
- conversation: {
- description:
- "A scrollable container that auto-sticks to the bottom as new messages arrive.",
- usage: `import {
- Conversation,
- ConversationContent,
- ConversationScrollButton,
-} from "ghost-ui";
-
-
-
- {messages.map((msg) => (
- ...
- ))}
-
-
- `,
- props: [
- {
- name: "initial",
- type: '"smooth" | "instant" | "auto"',
- default: '"smooth"',
- description: "Scroll behavior when the component first mounts.",
- },
- {
- name: "resize",
- type: '"smooth" | "instant" | "auto"',
- default: '"smooth"',
- description: "Scroll behavior when content resizes.",
- },
- ],
- composedWith: ["message", "prompt-input", "reasoning"],
- examples: [
- {
- name: "with-messages",
- title: "With Messages",
- description: "Conversation with multiple user and assistant messages.",
- },
- ],
- },
- "prompt-input": {
- description:
- "A composable prompt input form with file attachments, commands, screenshots, and submit handling.",
- usage: `import {
- PromptInput,
- PromptInputTextarea,
- PromptInputActions,
-} from "ghost-ui";
-
- console.log(msg)}>
-
-
- `,
- props: [
- {
- name: "onSubmit",
- type: "(message: PromptInputMessage, event: FormEvent) => void | Promise",
- description: "Called when the user submits the prompt.",
- },
- {
- name: "accept",
- type: "string",
- description: 'MIME filter for file attachments, e.g. "image/*".',
- },
- {
- name: "maxFiles",
- type: "number",
- description: "Maximum number of attachable files.",
- },
- {
- name: "maxFileSize",
- type: "number",
- description: "Maximum file size in bytes.",
- },
- {
- name: "globalDrop",
- type: "boolean",
- default: "false",
- description: "Accept file drops anywhere on the document.",
- },
- ],
- composedWith: ["conversation", "message", "attachments"],
- examples: [
- {
- name: "with-attachments",
- title: "With Attachments",
- description: "PromptInput showing file attachment controls.",
- },
- ],
- },
- reasoning: {
- description:
- "A collapsible thinking indicator that auto-opens during streaming and auto-closes when done.",
- usage: `import {
- Reasoning,
- ReasoningTrigger,
- ReasoningContent,
-} from "ghost-ui";
-
-
-
- The model's internal reasoning text...
- `,
- props: [
- {
- name: "isStreaming",
- type: "boolean",
- default: "false",
- description:
- "Whether the model is currently generating reasoning tokens.",
- },
- {
- name: "duration",
- type: "number",
- description:
- "Elapsed thinking time in seconds, shown in the trigger label.",
- },
- {
- name: "open",
- type: "boolean",
- description: "Controlled open state.",
- },
- {
- name: "defaultOpen",
- type: "boolean",
- description: "Initial open state when uncontrolled.",
- },
- ],
- composedWith: ["message", "conversation", "chain-of-thought"],
- examples: [],
- },
- "chain-of-thought": {
- description:
- "Displays a step-by-step breakdown of an AI model's reasoning process with collapsible detail.",
- usage: `import {
- ChainOfThought,
- ChainOfThoughtHeader,
- ChainOfThoughtContent,
- ChainOfThoughtStep,
-} from "ghost-ui";
-
-
- Reasoning steps
-
-
-
-
- `,
- props: [
- {
- name: "open",
- type: "boolean",
- description: "Controlled open state of the collapsible.",
- },
- {
- name: "defaultOpen",
- type: "boolean",
- default: "false",
- description: "Initial open state when uncontrolled.",
- },
- {
- name: "onOpenChange",
- type: "(open: boolean) => void",
- description: "Called when the open state changes.",
- },
- ],
- composedWith: ["message", "reasoning", "conversation"],
- examples: [],
- },
- "code-block": {
- description:
- "Syntax-highlighted code viewer powered by Shiki with copy-to-clipboard, line numbers, and language selection.",
- usage: `import {
- CodeBlock,
- CodeBlockHeader,
- CodeBlockActions,
- CodeBlockCopyButton,
-} from "ghost-ui";
-
-
-
- example.ts
-
-
-
-
- `,
- props: [
- {
- name: "code",
- type: "string",
- description: "The source code string to highlight and display.",
- },
- {
- name: "language",
- type: "BundledLanguage",
- description:
- 'The programming language for syntax highlighting (e.g. "tsx", "python").',
- },
- {
- name: "showLineNumbers",
- type: "boolean",
- default: "false",
- description: "Whether to display line numbers in the gutter.",
- },
- ],
- composedWith: ["message", "artifact", "terminal"],
- examples: [
- {
- name: "with-diff",
- title: "Multi-Language",
- description:
- "CodeBlock with a language selector for multiple snippets.",
- },
- ],
- },
- agent: {
- description:
- "Displays an AI agent configuration card with name, model, instructions, tools, and output schema.",
- usage: `import {
- Agent,
- AgentHeader,
- AgentContent,
- AgentInstructions,
-} from "ghost-ui";
-
-
-
-
- Find relevant papers on the topic.
-
- `,
- props: [
- {
- name: "children",
- type: "ReactNode",
- description:
- "Agent sub-components (AgentHeader, AgentContent, AgentTools, etc.).",
- },
- ],
- composedWith: ["code-block", "message"],
- examples: [],
- },
- terminal: {
- description:
- "A terminal emulator view with ANSI color support, auto-scroll, copy, and clear actions.",
- usage: `import {
- Terminal,
- TerminalHeader,
- TerminalTitle,
- TerminalContent,
-} from "ghost-ui";
-
- `,
- props: [
- {
- name: "output",
- type: "string",
- description: "The terminal output string, supports ANSI escape codes.",
- },
- {
- name: "isStreaming",
- type: "boolean",
- default: "false",
- description: "Shows a blinking cursor when true.",
- },
- {
- name: "autoScroll",
- type: "boolean",
- default: "true",
- description: "Automatically scroll to the bottom when output changes.",
- },
- {
- name: "onClear",
- type: "() => void",
- description:
- "Callback to clear the terminal; enables the clear button when provided.",
- },
- ],
- composedWith: ["code-block", "agent", "message"],
- examples: [],
- },
- "file-tree": {
- description:
- "An interactive file system tree with expandable folders, file selection, and custom icons.",
- usage: `import {
- FileTree,
- FileTreeFolder,
- FileTreeFile,
-} from "ghost-ui";
-
-
-
-
-
- `,
- props: [
- {
- name: "expanded",
- type: "Set",
- description: "Controlled set of expanded folder paths.",
- },
- {
- name: "defaultExpanded",
- type: "Set",
- description: "Initial set of expanded folder paths when uncontrolled.",
- },
- {
- name: "selectedPath",
- type: "string",
- description: "The currently selected file or folder path.",
- },
- {
- name: "onSelect",
- type: "(path: string) => void",
- description: "Called when a file or folder is selected.",
- },
- ],
- composedWith: ["artifact", "code-block"],
- examples: [],
- },
- artifact: {
- description:
- "A panel container for generated content with a header, close button, actions, and scrollable body.",
- usage: `import {
- Artifact,
- ArtifactHeader,
- ArtifactTitle,
- ArtifactContent,
- ArtifactClose,
-} from "ghost-ui";
-
-
-
- Generated Code
-
-
- ...
- `,
- props: [
- {
- name: "children",
- type: "ReactNode",
- description:
- "Artifact sub-components (ArtifactHeader, ArtifactContent, etc.).",
- },
- ],
- composedWith: ["code-block", "file-tree", "message"],
- examples: [],
- },
- context: {
- description:
- "A hover card displaying model context window usage, token breakdown, and cost estimation.",
- usage: `import {
- Context,
- ContextTrigger,
- ContextContent,
- ContextContentHeader,
-} from "ghost-ui";
-
-
-
-
-
-
- `,
- props: [
- {
- name: "usedTokens",
- type: "number",
- description: "Number of tokens used in the current context window.",
- },
- {
- name: "maxTokens",
- type: "number",
- description: "Maximum token capacity of the model.",
- },
- {
- name: "usage",
- type: "LanguageModelUsage",
- description:
- "Detailed token usage breakdown (input, output, reasoning, cache).",
- },
- {
- name: "modelId",
- type: "string",
- description: "Model identifier used for cost estimation via tokenlens.",
- },
- ],
- composedWith: ["prompt-input", "conversation"],
- examples: [],
- },
- plan: {
- description:
- "A collapsible card showing an AI-generated plan with streaming shimmer support.",
- usage: `import {
- Plan,
- PlanHeader,
- PlanTitle,
- PlanDescription,
- PlanContent,
-} from "ghost-ui";
-
-
-
- Implementation Plan
- 3 steps to complete the task
-
- ...
- `,
- props: [
- {
- name: "isStreaming",
- type: "boolean",
- default: "false",
- description:
- "Enables shimmer animation on title and description while streaming.",
- },
- {
- name: "open",
- type: "boolean",
- description: "Controlled collapsed/expanded state.",
- },
- {
- name: "defaultOpen",
- type: "boolean",
- description: "Initial open state when uncontrolled.",
- },
- ],
- composedWith: ["message", "conversation", "chain-of-thought"],
- examples: [],
- },
- button: {
- description:
- "A versatile button with multiple visual variants, sizes, and an icon-only appearance mode.",
- usage: `import { Button } from "ghost-ui";
-
-Click me `,
- props: [
- {
- name: "variant",
- type: '"default" | "destructive" | "outline" | "secondary" | "ghost" | "link"',
- default: '"default"',
- description: "Visual style of the button.",
- },
- {
- name: "size",
- type: '"default" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm"',
- default: '"default"',
- description: "Size preset controlling height and padding.",
- },
- {
- name: "appearance",
- type: '"default" | "icon"',
- default: '"default"',
- description:
- 'Set to "icon" for square icon-only buttons that scale with size.',
- },
- {
- name: "asChild",
- type: "boolean",
- default: "false",
- description:
- "Merge props onto child element instead of rendering a .",
- },
- ],
- composedWith: ["dialog", "card"],
- examples: [],
- },
- card: {
- description:
- "A content container with header, title, description, body, action slot, and footer sections.",
- usage: `import {
- Card,
- CardHeader,
- CardTitle,
- CardDescription,
- CardContent,
-} from "ghost-ui";
-
-
-
- Card Title
- Card description text.
-
- Body content here.
- `,
- props: [
- {
- name: "children",
- type: "ReactNode",
- description:
- "Card sub-components (CardHeader, CardContent, CardFooter, etc.).",
- },
- ],
- composedWith: ["button", "dialog"],
- examples: [],
- },
- dialog: {
- description:
- "A modal dialog overlay with title, description, header/footer sections, and close button.",
- usage: `import {
- Dialog,
- DialogTrigger,
- DialogContent,
- DialogHeader,
- DialogTitle,
- DialogDescription,
-} from "ghost-ui";
-
-
-
- Open
-
-
-
- Dialog Title
- Description text.
-
-
- `,
- props: [
- {
- name: "open",
- type: "boolean",
- description: "Controlled open state of the dialog.",
- },
- {
- name: "onOpenChange",
- type: "(open: boolean) => void",
- description: "Called when the dialog open state changes.",
- },
- ],
- composedWith: ["button", "card"],
- examples: [],
- },
- input: {
- description:
- "A styled text input with focus ring, validation states, and file input support.",
- usage: `import { Input } from "ghost-ui";
-
- `,
- props: [
- {
- name: "type",
- type: "string",
- default: '"text"',
- description: "HTML input type attribute.",
- },
- {
- name: "placeholder",
- type: "string",
- description: "Placeholder text shown when empty.",
- },
- ],
- composedWith: ["button", "label"],
- examples: [],
- },
- tabs: {
- description:
- "A tabbed interface for switching between panels of related content.",
- usage: `import { Tabs, TabsList, TabsTrigger, TabsContent } from "ghost-ui";
-
-
-
- Tab 1
- Tab 2
-
- Content 1
- Content 2
- `,
- props: [
- {
- name: "defaultValue",
- type: "string",
- description: "The value of the tab that should be active by default.",
- },
- {
- name: "value",
- type: "string",
- description: "Controlled active tab value.",
- },
- {
- name: "onValueChange",
- type: "(value: string) => void",
- description: "Called when the active tab changes.",
- },
- ],
- composedWith: ["card"],
- examples: [],
- },
-};
-
-export function getComponentDoc(slug: string): ComponentDoc | undefined {
- return docs[slug];
-}
diff --git a/apps/docs/src/lib/component-registry.ts b/apps/docs/src/lib/component-registry.ts
deleted file mode 100644
index 554a609a..00000000
--- a/apps/docs/src/lib/component-registry.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-import registryData from "ghost-ui/registry.json";
-
-// ── Category metadata ──
-// Display order and names for the functional categories in registry.json.
-// "ai" is a tag, not a navigation category — it's used as a badge instead.
-
-const categoryMeta: Record = {
- input: {
- name: "Inputs",
- description: "Form controls and interactive inputs",
- },
- display: {
- name: "Display",
- description: "Visual content and presentation",
- },
- feedback: {
- name: "Feedback",
- description: "Alerts, notifications, and status indicators",
- },
- navigation: {
- name: "Navigation",
- description: "Menus, tabs, and wayfinding",
- },
- layout: {
- name: "Layout",
- description: "Content organization and structure",
- },
- chat: { name: "Chat", description: "Conversational AI interfaces" },
- code: {
- name: "Code",
- description: "Code editing, display, and development tools",
- },
- media: { name: "Media", description: "Audio, video, and rich media" },
-};
-
-const categoryOrder = Object.keys(categoryMeta);
-
-// ── Types ──
-
-export type Category = {
- slug: string;
- name: string;
- description: string;
-};
-
-export type ComponentEntry = {
- slug: string;
- name: string;
- categories: string[];
- primaryCategory: string;
- dependencies: string[];
- registryDependencies: string[];
- isAI: boolean;
- /** Where the demo file lives, if it exists */
- demoSource: "primitives" | "ai-elements";
-};
-
-// ── Helpers ──
-
-function slugToDisplayName(slug: string): string {
- return slug
- .split("-")
- .map((w) => {
- // Special casing
- if (w === "otp") return "OTP";
- if (w === "jsx") return "JSX";
- if (w === "api") return "API";
- return w.charAt(0).toUpperCase() + w.slice(1);
- })
- .join(" ");
-}
-
-function getPrimaryCategory(cats: string[]): string {
- const functional = cats.filter((c) => c !== "ai");
- if (functional.length === 0) return "display";
- // Pick the one that appears first in our defined order
- for (const cat of categoryOrder) {
- if (functional.includes(cat)) return cat;
- }
- return functional[0];
-}
-
-// ── Derived data ──
-
-type RegistryItem = {
- name: string;
- type: string;
- dependencies?: string[];
- registryDependencies?: string[];
- files?: { type: string; target: string; path: string }[];
- categories?: string[];
-};
-
-const uiItems = (registryData.items as RegistryItem[]).filter(
- (item) => item.type === "registry:ui",
-);
-
-const componentMap = new Map();
-
-for (const item of uiItems) {
- const cats = item.categories ?? [];
- const isAI = cats.includes("ai");
-
- // Determine demo directory from the component's file path
- const filePath = item.files?.[0]?.path ?? "";
- const demoSource: "primitives" | "ai-elements" = filePath.includes(
- "ai-elements",
- )
- ? "ai-elements"
- : "primitives";
-
- const entry: ComponentEntry = {
- slug: item.name,
- name: slugToDisplayName(item.name),
- categories: cats,
- primaryCategory: getPrimaryCategory(cats),
- dependencies: item.dependencies ?? [],
- registryDependencies: (item.registryDependencies ?? []).filter(
- (d) => d !== "utils",
- ),
- isAI,
- demoSource,
- };
-
- componentMap.set(item.name, entry);
-}
-
-// Sort components alphabetically within each category
-const allComponents = Array.from(componentMap.values()).sort((a, b) =>
- a.slug.localeCompare(b.slug),
-);
-
-// ── Public API ──
-
-export const categories: Category[] = categoryOrder
- .filter((slug) => allComponents.some((c) => c.primaryCategory === slug))
- .map((slug) => ({
- slug,
- ...categoryMeta[slug],
- }));
-
-export function getAllComponents(): ComponentEntry[] {
- return allComponents;
-}
-
-export function getComponent(slug: string): ComponentEntry | undefined {
- return componentMap.get(slug);
-}
-
-export function getCategory(slug: string): Category | undefined {
- return categories.find((c) => c.slug === slug);
-}
-
-export function getComponentsByCategory(
- categorySlug: string,
-): ComponentEntry[] {
- return allComponents.filter((c) => c.primaryCategory === categorySlug);
-}
diff --git a/apps/docs/src/lib/component-source.ts b/apps/docs/src/lib/component-source.ts
deleted file mode 100644
index 7795cfab..00000000
--- a/apps/docs/src/lib/component-source.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import registryData from "ghost-ui/registry.json";
-
-// ── Types ──
-
-export type VariantDef = {
- name: string;
- values: string[];
- defaultValue?: string;
-};
-
-export type ComponentSpec = {
- /** All exported component/function names */
- exports: string[];
- /** CVA variant definitions, if any */
- variants: VariantDef[];
- /** data-slot values used in this component */
- dataSlots: string[];
- /** The raw source code */
- source: string;
- /** File path relative to project root */
- filePath: string;
-};
-
-type RegistryItem = {
- name: string;
- type: string;
- files?: { path: string }[];
-};
-
-// ── Raw source imports via Vite glob ──
-
-const rawSources: Record = {};
-
-const uiModules = import.meta.glob("/src/components/ui/**/*.tsx", {
- query: "?raw",
- eager: true,
-}) as Record;
-
-const aiModules = import.meta.glob("/src/components/ai-elements/**/*.tsx", {
- query: "?raw",
- eager: true,
-}) as Record;
-
-for (const [key, mod] of Object.entries({ ...uiModules, ...aiModules })) {
- // Glob keys are like "/src/components/ui/button.tsx"
- // Registry paths are like "src/components/ui/button.tsx"
- const normalized = key.startsWith("/") ? key.slice(1) : key;
- rawSources[normalized] = mod.default;
-}
-
-// ── Extraction helpers ──
-
-function extractExports(source: string): string[] {
- // Match named exports at the end of file: export { Foo, Bar, baz }
- const exportBlockRe = /export\s*\{([^}]+)\}/g;
- const exports: string[] = [];
-
- let match;
- while ((match = exportBlockRe.exec(source)) !== null) {
- const names = match[1].split(",").map((s) =>
- s
- .trim()
- .split(/\s+as\s+/)
- .pop()!
- .trim(),
- );
- exports.push(...names.filter(Boolean));
- }
-
- // Match: export function Foo / export const Foo
- const exportDeclRe =
- /export\s+(?:function|const|class)\s+([A-Z][A-Za-z0-9]*)/g;
- while ((match = exportDeclRe.exec(source)) !== null) {
- if (!exports.includes(match[1])) {
- exports.push(match[1]);
- }
- }
-
- return exports;
-}
-
-function extractVariants(source: string): VariantDef[] {
- // Find the CVA variants block
- const variantsBlockRe =
- /variants:\s*\{([\s\S]*?)\},\s*(?:compoundVariants|defaultVariants)/;
- const variantsMatch = variantsBlockRe.exec(source);
- if (!variantsMatch) return [];
-
- const variantsBlock = variantsMatch[1];
-
- // Find default variants
- const defaultsRe = /defaultVariants:\s*\{([^}]*)\}/;
- const defaultsMatch = defaultsRe.exec(source);
- const defaults: Record = {};
-
- if (defaultsMatch) {
- const defaultEntries = defaultsMatch[1].matchAll(
- /(\w[\w-]*)\s*:\s*"([^"]*)"/g,
- );
- for (const entry of defaultEntries) {
- defaults[entry[1]] = entry[2];
- }
- }
-
- // Parse each variant group: variantName: { value1: "...", value2: "..." }
- const variantDefs: VariantDef[] = [];
- const variantGroupRe = /(\w[\w-]*)\s*:\s*\{/g;
- let groupMatch;
-
- while ((groupMatch = variantGroupRe.exec(variantsBlock)) !== null) {
- const variantName = groupMatch[1];
- // Find the matching closing brace
- let depth = 1;
- let i = groupMatch.index + groupMatch[0].length;
- while (i < variantsBlock.length && depth > 0) {
- if (variantsBlock[i] === "{") depth++;
- if (variantsBlock[i] === "}") depth--;
- i++;
- }
- const groupContent = variantsBlock.slice(
- groupMatch.index + groupMatch[0].length,
- i - 1,
- );
-
- // Extract value names (keys of the object, anchored to line start to skip Tailwind prefixes like hover:)
- const valueRe = /^\s*["']?([\w-]+)["']?\s*:/gm;
- const values: string[] = [];
- let valueMatch;
- while ((valueMatch = valueRe.exec(groupContent)) !== null) {
- if (!values.includes(valueMatch[1])) {
- values.push(valueMatch[1]);
- }
- }
-
- if (values.length > 0) {
- variantDefs.push({
- name: variantName,
- values,
- defaultValue: defaults[variantName],
- });
- }
- }
-
- return variantDefs;
-}
-
-function extractDataSlots(source: string): string[] {
- const slotRe = /data-slot="([^"]+)"/g;
- const slots: string[] = [];
- let match;
- while ((match = slotRe.exec(source)) !== null) {
- if (!slots.includes(match[1])) {
- slots.push(match[1]);
- }
- }
- return slots;
-}
-
-// ── Public API ──
-
-export function getComponentSpec(slug: string): ComponentSpec | null {
- const items = registryData.items as RegistryItem[];
- const item = items.find((i) => i.name === slug && i.type === "registry:ui");
- if (!item?.files?.[0]?.path) return null;
-
- const filePath = item.files[0].path;
- const source = rawSources[filePath];
- if (!source) return null;
-
- return {
- exports: extractExports(source),
- variants: extractVariants(source),
- dataSlots: extractDataSlots(source),
- source,
- filePath,
- };
-}
diff --git a/apps/docs/src/main.tsx b/apps/docs/src/main.tsx
index db0ea1a7..8c6c9d87 100644
--- a/apps/docs/src/main.tsx
+++ b/apps/docs/src/main.tsx
@@ -1,7 +1,7 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router";
-import "ghost-ui/styles.css";
+import "@design-intelligence/vessel/styles.css";
import { App } from "./App";
import "./styles/dev-fonts.css";
diff --git a/biome.json b/biome.json
index bda865e8..43ba5ec2 100644
--- a/biome.json
+++ b/biome.json
@@ -35,7 +35,7 @@
},
"overrides": [
{
- "includes": ["packages/ghost-ui/**", "apps/docs/**"],
+ "includes": ["packages/vessel/**", "apps/docs/**"],
"linter": {
"enabled": false
}
diff --git a/docs/agent-readable-brand-steering.md b/docs/agent-readable-brand-steering.md
new file mode 100644
index 00000000..4730ec4a
--- /dev/null
+++ b/docs/agent-readable-brand-steering.md
@@ -0,0 +1,1023 @@
+# Model Steering for Agent-Readable Brand Systems
+
+> **Prose, exemplar materials, and invariants are the right starting triad, but they are not the whole steering system.**
+> The fuller model is: **retrieval → conditioning → commitment → constraint → review → renewal.**
+
+> Research grounding for the claims in this doc — with honest evidence tiers —
+> lives in [research-references.md](./research-references.md).
+
+Brand work is moving from static outputs to **agent-readable input systems**. Ghost is the operational version: making those inputs **selectable, grounded, ratified, and reviewable**.
+
+The core theory:
+
+> A model does not “know your brand” as a stable object. It generates the next token from learned priors plus current context. Authoring works by changing what context gets selected, what patterns are easiest to continue, what paths are disallowed, and what gets checked after generation.
+
+So the question is not only “what should we tell the model?”
+
+It is:
+
+1. **Will the right truth be retrieved?**
+2. **Will the model understand what is normative?**
+3. **Will it have concrete material to continue from?**
+4. **Will it avoid generic gravity wells?**
+5. **Will it commit to the right structure early?**
+6. **Will hard lines be preserved?**
+7. **Will drift be caught after generation?**
+8. **Where does human originality enter instead of median recombination?**
+
+---
+
+## 1. Retrieval handles
+
+### What they are
+
+The parts that help the agent decide what to read before generation:
+
+- node id
+- filename/kind
+- `description`
+- glossary category
+- `index.md`
+- task-shaped language
+
+In Ghost, this is the `ghost gather` / `ghost pull` layer.
+
+### How they influence generation
+
+They do not directly affect the final output unless they get pulled into context.
+
+This is the first steering gate:
+
+> If the relevant truth is not retrieved, it has zero effect.
+
+A lot of “the model ignored the brand” is actually “the model never saw the right node.”
+
+### How to think of them
+
+Think of descriptions like tool descriptions or search snippets.
+
+They should answer:
+
+> “When should an agent realize this applies?”
+
+Not:
+
+> “What is this poetically about?”
+
+### Weak example
+
+```yaml
+description: Our trust principles.
+```
+
+### Strong example
+
+```yaml
+description: Checkout trust — gather for payment, pricing, fees, irreversible actions, disclosure, risk, or user hesitation before commitment.
+```
+
+### How to author
+
+Use the words future asks will contain:
+
+- checkout
+- pricing
+- empty state
+- failure
+- onboarding
+- dashboard
+- destructive action
+- motion
+- logo
+- campaign
+- launch
+- recovery
+- disclosure
+
+Good format:
+
+```yaml
+description: — gather when .
+```
+
+---
+
+## 2. Stance prose / principles
+
+### What they are
+
+The broad interpretive truths:
+
+- what the brand is for
+- who it serves
+- what it values
+- what tradeoff wins
+- how it behaves under pressure
+- what it refuses to become
+
+This is the classic prose layer.
+
+### How they influence generation
+
+Prose changes the model’s **semantic prior**.
+
+It nudges the model toward a region of its learned distribution. But prose is coarse. Words like “premium,” “clean,” “bold,” “playful,” or “modern” activate generic learned associations.
+
+So prose is weakest when it uses adjectives. It is strongest when it encodes **decision logic**.
+
+### How to think of it
+
+Prose answers:
+
+> “When the model has to invent, what should it optimize for?”
+
+It gives decision logic, not exact form.
+
+### Weak example
+
+```markdown
+We are modern, trustworthy, simple, and human.
+```
+
+This excludes almost nothing.
+
+### Strong example
+
+```markdown
+At moments of financial commitment, reduce felt risk before increasing momentum. The user should understand what will happen, what it costs, and how to recover before the primary action asks for trust. Clarity beats speed when the action is irreversible.
+```
+
+That gives the model a tradeoff.
+
+### How to author
+
+Write as forced choices:
+
+```markdown
+When X conflicts with Y, X wins.
+```
+
+Examples:
+
+```markdown
+When density and spaciousness conflict on operational surfaces, density wins — but hierarchy must come from alignment and weight before color.
+
+When trust and conversion pressure conflict, trust wins. Never hide risk to make the primary action feel easier.
+
+When delight and comprehension conflict, comprehension wins. Motion may clarify state change; it must not become decoration.
+```
+
+Avoid brand-book filler. A principle should make the model reject a plausible wrong answer.
+
+---
+
+## 3. Concrete inventory / agent-readable materials
+
+### What they are
+
+The concrete things the model should use instead of guessing:
+
+- CSS variables
+- tokens
+- fonts
+- SVGs
+- components
+- HTML examples
+- motion JSON
+- copy snippets
+- implementation paths
+- Figma links
+- screenshots
+- asset folders
+
+HTML/CSS/SVG/JSON are not just documentation. They are **buildable context**.
+
+### How they influence generation
+
+Inventory solves knowledge gaps.
+
+If the model does not know your component names, token values, logo files, motion curves, or type ramp, it will invent plausible ones.
+
+No amount of “stay on brand” fixes missing facts.
+
+### How to think of it
+
+Inventory answers:
+
+> “What should the model not have to invent?”
+
+This is the factual layer.
+
+### Example
+
+```markdown
+---
+description: Product color, type, and motion materials — gather before building UI, motion demos, or branded surfaces.
+materials:
+ - src/styles/tokens.css
+ - src/components/brand/**
+ - src/motion/type-curves.json
+ - public/brand/logo*.svg
+---
+
+Use the product tokens rather than ad hoc values. Primary actions use `--color-action-primary`; never introduce a new blue because a generic button pattern expects one. Motion uses the curves in `src/motion/type-curves.json`; do not approximate easing with default `ease-in-out`.
+```
+
+### How to author
+
+Use inventory when the model keeps:
+
+- inventing colors
+- inventing components
+- using generic Tailwind defaults
+- approximating motion
+- missing asset rules
+- creating new styles instead of using existing ones
+
+But do not turn the fingerprint into duplicated API docs.
+
+Bad:
+
+```markdown
+The Button component takes size, variant, iconLeft, iconRight...
+```
+
+Better:
+
+```markdown
+Primary actions use the existing Button primitive. The brand move is not the API; it is restraint: one primary action per surface, no competing accent treatment, and no new button color outside the token ramp.
+```
+
+`materials` locate. Prose explains meaning.
+
+---
+
+## 4. Exemplars
+
+### What they are
+
+Concrete examples of good output:
+
+- a real screen
+- a launch page
+- a component implementation
+- an HTML artifact
+- an error message
+- a motion demo
+- a product flow
+- a campaign module
+
+These can live directly in prose or be pointed to through `materials`.
+
+### How they influence generation
+
+Exemplars are often the strongest generation-time signal.
+
+Mechanically, they work like in-context demonstrations. The model sees structure, rhythm, naming, spacing, hierarchy, tone, and code shape, then continues in that pattern.
+
+This is why HTML/CSS examples are so powerful: they expose structure, not just appearance.
+
+A screenshot says:
+
+> “Look at this.”
+
+HTML/CSS says:
+
+> “Continue from this.”
+
+### How to think of them
+
+Exemplars answer:
+
+> “What does good look like?”
+
+But they need annotation. Otherwise the model copies accidents as if they were brand.
+
+### Example
+
+```markdown
+---
+description: On-brand failure copy — gather for errors, failed saves, retries, and recovery states.
+---
+
+Normative for rhythm and stance at failure moments. Match the structure, not the exact words.
+
+> We couldn't save your changes. Your work is still here — try again, and if it keeps failing, we'll hold onto everything while you sort it out.
+
+What makes it ours:
+- Leads with what happened, not apology.
+- States what is safe before what to do.
+- Gives one calm next step.
+- No "Oops."
+- No exclamation points.
+- No blame on the user or the network.
+```
+
+### How to author
+
+For every exemplar, say:
+
+1. what it is normative for;
+2. what is incidental;
+3. what features make it on-brand;
+4. when to reuse the pattern;
+5. when not to.
+
+Good phrase:
+
+```markdown
+This sample is normative for , not for .
+```
+
+---
+
+## 5. Counter-exemplars / anti-goals
+
+### What they are
+
+Specific rejected patterns:
+
+- generic SaaS cards
+- bootstrap-blue buttons
+- gradient hero text
+- emoji headings
+- “Oops!” error copy
+- fake dashboard previews
+- over-rounded cards
+- decorative motion
+- three equal CTAs
+
+### How they influence generation
+
+Anti-goals fight model gravity wells.
+
+Models regress toward common training patterns. If you ask for “modern landing page,” you will get something like:
+
+- gradient headline
+- rounded cards
+- soft shadows
+- three-column feature grid
+- blue/purple accent
+- generic dashboard mockup
+
+Positive prose often does not escape this. Specific negation does.
+
+### How to think of it
+
+Anti-goals answer:
+
+> “Which likely continuation should the model refuse?”
+
+They prune the distribution.
+
+### Example
+
+```markdown
+---
+description: Generic generated UI we reject — gather before building product UI or marketing surfaces.
+---
+
+We are not the generic generated interface: no indigo-600 primary buttons, no rounded-xl white cards floating on gray-50, no emoji in headings, no gradient hero text, no fake dashboard preview unless the product moment is actually dashboard-shaped.
+
+When a layout feels like a template any product could ship, it is off-brand even if every token is technically valid.
+
+Default away from that gravity well: flat surfaces, hard alignment, one accent used sparingly, and hierarchy from spacing before decoration.
+```
+
+### How to author
+
+Do not say:
+
+```markdown
+Avoid generic design.
+```
+
+Say:
+
+```markdown
+No rounded-xl card grids on gray backgrounds. No gradient hero type. No decorative blob backgrounds. No fake analytics dashboard unless analytics is the actual product object.
+```
+
+Name the enemy.
+
+---
+
+## 6. Invariants
+
+### What they are
+
+Hard always/never rules:
+
+- one primary CTA
+- fees before commitment
+- no new colors outside tokens
+- logo never redrawn
+- error states always include recovery
+- motion respects reduced-motion
+- type hierarchy never inverts
+- children never outrank parent context
+
+### How they influence generation
+
+Invariants constrain the search space.
+
+They are strongest when they are:
+
+- short;
+- concrete;
+- checkable;
+- numeric where possible;
+- written as always/never;
+- placed early in the brief;
+- mirrored by review checks.
+
+### How to think of them
+
+Invariants answer:
+
+> “What must survive every interpretation?”
+
+They are the floor.
+
+### Example
+
+```markdown
+---
+description: Checkout trust invariants — hard rules for payment, pricing, and irreversible commitment.
+---
+
+These hold for every checkout or payment-adjacent surface:
+
+- Fees are disclosed before the committing action. Never reveal a new fee after the primary CTA.
+- At most one primary action appears in a checkout step.
+- Reassurance sits near the action it supports; never isolate trust copy in a generic footer.
+- Recovery is visible wherever failure is possible: retry, edit, cancel, or contact support.
+- The primary price and the committing action appear in the same viewport before commitment.
+```
+
+### How to author
+
+Use invariant form when the failure is a repeated review comment.
+
+Good syntax:
+
+```markdown
+- Always when .
+- Never ; instead .
+- At most/exactly/no more than .
+```
+
+If it cannot be reviewed, it may not be an invariant yet.
+
+---
+
+## 7. Composition patterns
+
+### What they are
+
+Reusable structures that bind part of the output and leave part open.
+
+This is the “pattern” layer:
+
+- empty state
+- status with next step
+- pricing comparison
+- checkout disclosure
+- confirmation
+- onboarding step
+- launch hero
+- feature proof
+- destructive action modal
+
+### How they influence generation
+
+Patterns steer early commitment.
+
+Autoregressive generation commits quickly. Once the model starts a three-card grid, modal, dashboard, or hero section, the rest of the output gets pulled into that structure.
+
+A composition pattern gives the model a better starting scaffold.
+
+### How to think of it
+
+Patterns answer:
+
+> “What shape should this kind of thing take before details are chosen?”
+
+They sit between principle and exemplar.
+
+### Example
+
+```markdown
+---
+description: Status plus next step — gather when the user asks where something stands and what to do now.
+---
+
+**Applies when:** the surface explains the state of something in flight: an order, transfer, review, delivery, setup, or request.
+
+**Bound:**
+
+- Current status renders first and largest.
+- One sentence explains why the status matters.
+- Exactly one primary next step appears last.
+- Supporting evidence may not compete with the status.
+
+**Open:**
+
+- Evidence may be a timeline, timestamp, or compact stat list.
+- Tone may flex warmer for consumer surfaces and more direct for operational ones.
+- Secondary actions may appear only after the primary next step.
+
+**Refines:** `principle.composition`. If this conflicts with the composition floor, the floor wins.
+```
+
+### How to author
+
+Use the bound/open distinction.
+
+- **Bound** = do not redecide.
+- **Open** = the agent may choose, within limits.
+
+If everything is bound, you wrote a template.
+If nothing is bound, you wrote vibes.
+
+---
+
+## 8. Conditions and altitude
+
+### What they are
+
+Scope rules for when a truth applies.
+
+A universal truth is stated plainly. A narrower truth names the situation that activates it.
+
+Ghost is right that this belongs in prose, not hierarchy.
+
+### How they influence generation
+
+Conditions prevent truth smearing.
+
+Without conditions, the model applies good guidance everywhere:
+
+- checkout trust leaks into marketing;
+- operational density leaks into onboarding;
+- failure voice leaks into success copy;
+- launch-page drama leaks into settings UI.
+
+### How to think of it
+
+Conditions answer:
+
+> “When does this truth fire?”
+
+### Weak example
+
+```markdown
+For dashboards, use compact density.
+```
+
+### Strong example
+
+```markdown
+When the user's first question is comparative — “which record needs attention?” — density may increase by one step. Even then, hierarchy comes from alignment and weight before color.
+```
+
+The second can apply outside literal dashboards and avoid applying inside dashboards when the situation does not match.
+
+### How to author
+
+Use condition language:
+
+```markdown
+When the user is about to commit money...
+When recognition matters more than space...
+When the surface must compare many records...
+When the action is irreversible...
+When the brand should recede behind the task...
+```
+
+Avoid pure destination buckets:
+
+```markdown
+for dashboards
+for mobile
+for marketing
+for emails
+```
+
+Those are hints, not the real condition.
+
+---
+
+## 9. Decision traces / procedures
+
+This bucket is underweighted in many brand systems.
+
+### What they are
+
+Worked reasoning examples showing how a brand decision was made.
+
+Not just:
+
+> “Use dense layouts.”
+
+But:
+
+> “We considered a drawer, inline chips, and a command palette. We chose command palette because density beats discoverability in this operational moment.”
+
+### How they influence generation
+
+They teach the model the **tradeoff function**.
+
+Models imitate reasoning traces as well as outputs. A decision trace shows how the brand resolves conflict.
+
+This matters because two brands can share:
+
+- the same tokens;
+- similar components;
+- similar layouts;
+- similar voice adjectives;
+
+but differ completely in how they decide under pressure.
+
+### How to think of it
+
+Decision traces answer:
+
+> “How do we choose when two good options conflict?”
+
+This is brand as taste under pressure.
+
+### Example
+
+```markdown
+---
+description: How we choose dense operational controls — gather when filtering, searching, or narrowing large record sets.
+---
+
+Decision trace:
+
+We needed filtering for a high-volume review queue.
+
+Options considered:
+- Drawer: clearer, but too heavy for repeated use.
+- Inline chips: visible, but noisy after five filters.
+- Command palette: less discoverable, but fastest for expert operators.
+
+We chose command palette because this surface is used repeatedly by trained operators. In this condition, speed and density beat first-use discoverability. On consumer onboarding, this decision would reverse.
+```
+
+### How to author
+
+Ask during fingerprint interviews:
+
+- What decision did you reverse?
+- What almost shipped but felt wrong?
+- What did you choose between?
+- Which value won?
+- When would that decision reverse?
+- What did a senior designer catch that others missed?
+
+This captures the “taste move,” not just the artifact.
+
+---
+
+## 10. Checks and review assertions
+
+### What they are
+
+Post-generation tests or review prompts.
+
+In Ghost, these live under the checks directory:
+
+```text
+.ghost/checks/*.md
+```
+
+They are feed-back, not feed-forward. They do not appear in `ghost gather` or `ghost pull`.
+
+### How they influence generation
+
+Strictly speaking, checks do not steer the first generation unless the harness uses them in a loop.
+
+They steer the system by creating:
+
+```text
+generate → review → repair → review again
+```
+
+That is often more reliable than trying to make the first sample perfect.
+
+### How to think of it
+
+Checks answer:
+
+> “How do we know the truth survived implementation?”
+
+They turn invariants into reviewable assertions.
+
+### Example
+
+```markdown
+---
+name: checkout-fees-before-commitment
+description: Checkout discloses fees before the committing action.
+severity: high
+references:
+ - principle.checkout-trust
+---
+
+Grade whether the change preserves the rule that fees are disclosed before the committing action. Flag any flow where a new fee appears after the primary CTA, where fee text is visually detached from the action, or where the user must expand optional UI to discover required cost.
+```
+
+### How to author
+
+Every hard “never” or “always” should be considered for a check.
+
+Good checks say:
+
+- what to inspect;
+- what to flag;
+- what counts as preserved;
+- which node grounds the assertion.
+
+Do not invent new obligations in checks. They should reference the brand truth they enforce.
+
+---
+
+## 11. Context assembly / brief shape
+
+### What it is
+
+The way selected truths are assembled before generation.
+
+This is not cosmetic. Order matters.
+
+The first few commitments in a generated output strongly shape everything after. If the model starts from a generic scaffold, later brand instructions have to fight that scaffold.
+
+### How it influences generation
+
+A good brief controls what the model commits to first.
+
+Useful order:
+
+1. grounded nodes;
+2. non-negotiables;
+3. applicable conditions;
+4. intent;
+5. inventory/materials;
+6. composition pattern;
+7. exemplars;
+8. anti-goals;
+9. silent/provisional areas.
+
+### How to think of it
+
+Context assembly answers:
+
+> “What should the model anchor on before it starts producing?”
+
+### Example
+
+```markdown
+## Grounded in
+
+Pulled: `index`, `principle.checkout-trust`, `pattern.status-with-next-step`, `anti-goal.generic-ui`.
+
+## Non-negotiables
+
+- Fees before commitment. [`principle.checkout-trust`]
+- At most one primary action per step. [`principle.checkout-trust`]
+- No generic rounded-card SaaS layout. [`anti-goal.generic-ui`]
+
+## Conditions that apply
+
+This is a payment-adjacent commitment moment, so checkout trust rules apply.
+
+## Intent
+
+Reduce felt risk before increasing momentum. Clarity beats speed.
+
+## Inventory
+
+Use existing checkout components and token ramp. Do not introduce new button styles.
+
+## Composition
+
+Current state first, one line of meaning, one next step last.
+
+## Silent / provisional
+
+The fingerprint does not specify loading skeletons. Follow local code convention provisionally.
+```
+
+### How to author
+
+The fingerprint should make this easy:
+
+- keep nodes purpose-coherent;
+- make descriptions selectable;
+- put true global non-negotiables in `index`;
+- distinguish hard rules from soft guidance;
+- label silence.
+
+---
+
+## 12. Deliberate novelty
+
+### What it is
+
+The non-median creative move:
+
+- the weird launch concept;
+- the memorable above-the-fold idea;
+- the campaign hook;
+- the specific idea the system would not have produced from recombination alone.
+
+### How they influence generation
+
+They disrupt median convergence.
+
+A well-authored brand system makes outputs coherent. But coherence is not originality. Models recombine what they are given and drift toward the center of allowed possibilities.
+
+Deliberate novelty is human injection of a surprising idea.
+
+### How to think of it
+
+Deliberate novelty answers:
+
+> “What makes this specific output memorable, not merely correct?”
+
+It is usually task-specific, not permanent brand law.
+
+### Example
+
+```markdown
+---
+description: Launch-specific creative hook — use only for the Type launch page.
+---
+
+For this launch, the hero should feel like entering a shared writing room after midnight: cursors moving, fragments collecting, multiple minds arriving at one sentence. This is a one-time campaign idea, not a reusable product UI pattern.
+
+Preserve:
+- multiplayer atmosphere;
+- language as material;
+- visible collaboration;
+- slightly nocturnal energy.
+
+Do not generalize this into the core product UI.
+```
+
+### How to author
+
+Keep deliberate novelty distinct from invariants.
+
+A one-time creative move can become an exemplar later, but do not accidentally turn a campaign-specific idea into permanent brand law.
+
+Ask:
+
+- Is this reusable truth?
+- Or is this a specific creative leap for one moment?
+
+Both are valuable. They should not be stored with the same normative weight.
+
+---
+
+## The updated steering stack
+
+| Bucket | What it is | Model mechanism | Best for |
+|---|---|---|---|
+| Retrieval handles | Descriptions, ids, glossary, index | Gets right context selected | Making truths findable |
+| Stance prose | Principles and tradeoffs | Shifts semantic prior | Decision-making in novel cases |
+| Inventory/materials | Tokens, components, HTML, CSS, SVG, JSON | Fixes knowledge gaps | Preventing invention |
+| Exemplars | Good outputs in context | Pattern continuation / few-shot shaping | Form, rhythm, code shape |
+| Anti-goals | Rejected defaults | Prunes generic priors | Escaping AI sameness |
+| Invariants | Always/never rules | Constrains generation | Hard brand/product lines |
+| Patterns | Bound/open structures | Controls early commitment | Reusable UI/content shapes |
+| Conditions | Applicability rules | Gates truths | Preventing over-application |
+| Decision traces | Worked tradeoff reasoning | Teaches choice function | Taste under conflict |
+| Checks | Review assertions | Feedback loop | Catching drift |
+| Brief assembly | Ordered context packet | Controls first commitments | Reliable generation setup |
+| Deliberate novelty | Task-specific creative leap | Breaks median recombination | Memorable creative output |
+
+---
+
+## How to update the original triad
+
+The original triad:
+
+> prose, exemplar materials, invariants
+
+Expanded:
+
+> **Prose for decision logic, materials for facts, exemplars for form, anti-goals for escaping priors, patterns for structure, invariants for hard boundaries, decision traces for taste, checks for drift, and deliberate novelty for non-median originality.**
+
+Or shorter:
+
+> **Say the stance. Show the material. Name the enemy. Bind the structure. State the hard lines. Show how decisions are made. Check the result. Leave room for the non-obvious idea.**
+
+---
+
+## A practical authoring template
+
+A strong Ghost-style node could look like this:
+
+```markdown
+---
+description:
+materials:
+ -
+---
+
+
+
+**Applies when:**
+
+**Preserve:**
+-
+-
+
+**Never:**
+-
+-
+
+**Bound:**
+-
+
+**Open:**
+-
+
+**Use / inspect:**
+-
+
+**Example:**
+>
+
+What makes this example intentional:
+-
+-
+-
+
+**Decision trace:**
+We chose over because . This would reverse when .
+```
+
+Not every node needs every section. But this template forces the author to ask the right questions.
+
+---
+
+## The diagnostic
+
+When the model fails, do not immediately add more prose. Classify the failure.
+
+### It invented a component, token, color, or asset
+
+Add **inventory/materials/exact values**.
+
+### It looked generic
+
+Add **exemplars and anti-goals**.
+
+### It chose the wrong structure
+
+Add a **composition pattern**.
+
+### It crossed a hard line
+
+Add an **invariant** and probably a **check**.
+
+### It applied good guidance in the wrong place
+
+Add **conditions / altitude**.
+
+### It made a technically correct but forgettable output
+
+Add a **specific creative concept** or stronger non-obvious idea.
+
+### It did not use an existing truth
+
+Fix the **description / retrieval handle**.
+
+### It made a bad tradeoff
+
+Add a **decision trace**.
+
+---
+
+## Final mental model
+
+Brand steering for agents is not “documentation the model reads.”
+
+It is closer to a layered control system:
+
+1. **Retrieval** decides what enters context.
+2. **Prose** sets decision logic.
+3. **Materials** provide facts.
+4. **Exemplars** teach form.
+5. **Anti-goals** suppress generic priors.
+6. **Patterns** control structural commitment.
+7. **Invariants** define hard boundaries.
+8. **Decision traces** teach taste.
+9. **Checks** catch drift.
+10. **Deliberate novelty** injects originality beyond recombination.
+
+That is the theory behind why prose, exemplar materials, and invariants matter — and why they are necessary but not sufficient.
diff --git a/docs/fingerprint-format.md b/docs/fingerprint-format.md
deleted file mode 100644
index 842faca5..00000000
--- a/docs/fingerprint-format.md
+++ /dev/null
@@ -1,274 +0,0 @@
-# The Portable Fingerprint Package Format
-
-A Ghost fingerprint is a checked-in, repo-local surface-composition contract
-that humans can approve and agents can act from. The canonical portable package
-lives under `.ghost/`:
-
-```text
-.ghost/
- manifest.yml # ghost.fingerprint-package/v1 package anchor
- intent.yml # core: surface intent
- inventory.yml # core: curated material and source links
- composition.yml # core: experience patterns
- validate.yml # optional deterministic gates
-```
-
-Git is the staging and approval boundary: uncommitted or unmerged edits are
-draft work, and checked-in facet files are canonical for Ghost.
-
-`manifest.yml` is intentionally small:
-
-```yaml
-schema: ghost.fingerprint-package/v1
-id: local
-```
-
-The raw facet files can be sparse. Missing files or sections mean this package
-contributes no local guidance for that facet; broader stack context may still
-supply it. Ghost normalizes absent facets to empty values when it assembles the
-internal `ghost.fingerprint/v1` document used by validation checks, review
-packets, Relay briefs, compare, and stack merges.
-
-## Core Facets
-
-`intent.yml` captures the intent behind the surface:
-
-```yaml
-summary:
- product: Example Docs
- audience: [contributors, maintainers]
- goals:
- - Preserve task-first documentation and product trust.
-principles:
- - id: intent-before-material
- principle: Intent captures the intent behind the surface; inventory points to replaceable material.
-experience_contracts:
- - id: review-cites-memory
- contract: Advisory review findings must cite the diff and relevant fingerprint refs.
-```
-
-Use intent for durable claims about audience needs, product obligations,
-acceptable tradeoffs, what the surface refuses to become, and contracts that
-should shape agent behavior.
-
-High-quality facet content makes generation choices explicit: what to preserve,
-what to avoid, which tradeoffs win, which situations route guidance, and which
-exemplars ground the claim.
-
-`inventory.yml` points to curated material and optional source links:
-
-```yaml
-topology:
- scopes:
- - id: docs-site
- paths: [apps/docs]
- surface_types: [docs-home, reference-page]
- surface_types: [docs-home, reference-page]
-building_blocks:
- tokens: [--color-bg, --color-fg]
- components: [Button, CodeBlock]
- libraries: [packages/ghost-ui]
- assets: [apps/docs/public/placeholder.svg]
- routes: [/docs, /docs/cli-reference]
- files: [apps/docs/src/content/docs/cli-reference.mdx]
-exemplars:
- - id: cli-reference-page
- path: apps/docs/src/content/docs/cli-reference.mdx
- title: CLI reference page
- surface_type: reference-page
- scope: docs-site
- why: Shows command facts and examples kept visually adjacent.
- refs: [composition.pattern:reference-before-decoration]
-sources:
- - id: ghost-ui-registry
- kind: registry
- ref: packages/ghost-ui/public/r/registry.json
-```
-
-Supported `inventory.sources[].kind` values are `registry`, `file`, `url`, and
-`package`. Source links are provenance and orientation; they do not
-make generated material canonical by themselves.
-
-`composition.yml` captures the patterns that make a surface feel intentional:
-
-```yaml
-patterns:
- - id: reference-before-decoration
- kind: structure
- pattern: Reference pages prioritize the working surface before visual flourish.
- applies_to:
- surface_types: [reference-page]
- guidance:
- - Keep command names, flags, and examples visually adjacent.
- - Put caveats near the command they modify.
- evidence:
- - path: apps/docs/src/content/docs/cli-reference.mdx
- check_refs: [validate.check:no-hardcoded-brand-color]
-```
-
-Pattern `kind` can be `rule`, `layout`, `structure`, `flow`, `state`,
-`visual`, `behavior`, or `content`.
-
-## References
-
-Use facet-qualified refs when one part of the fingerprint grounds another:
-
-- `intent.situation:`
-- `intent.principle:`
-- `intent.experience_contract:`
-- `inventory.exemplar:`
-- `composition.pattern:`
-- `validate.check:`
-
-Facet refs without `validate.check:` are used where only fingerprint facet material is
-valid, such as `inventory.exemplars[].refs`.
-
-## Enforcement
-
-`validate.yml` uses `ghost.validate/v1`. Checks are
-deterministic validation, not generation input.
-
-```yaml
-schema: ghost.validate/v1
-id: example-docs
-checks:
- - id: no-hardcoded-brand-color
- title: Use semantic color tokens
- status: active
- severity: serious
- derivation:
- intent:
- - intent.principle:reference-before-decoration
- inventory:
- - inventory.exemplar:cli-reference-page
- composition:
- - composition.pattern:reference-before-decoration
- applies_to:
- scopes: [docs-site]
- paths: [apps/docs]
- surface_types: [reference-page]
- detector:
- type: forbidden-regex
- pattern: '#[0-9a-fA-F]{3,8}'
- evidence:
- support: 0.92
- observed_count: 12
- examples:
- - apps/docs/src/styles/theme.css
- repair: Move repeatable colors into semantic tokens.
-```
-
-Check `status` can be `active`, `proposed`, or `disabled`. `severity` can be
-`critical`, `serious`, or `nit`.
-
-Detector `type` can be:
-
-- `forbidden-regex`
-- `required-regex`
-- `banned-import`
-- `banned-component`
-- `required-token`
-
-Ref-backed checks are preferred. Missing derivation refs lint as warnings, not
-errors, so teams can draft gates while curation catches up. Promote only rules
-that can be detected deterministically; taste stays in intent or composition
-until there is a reliable detector.
-
-## Nested Packages
-
-Large repos can add scoped packages below the root:
-
-```text
-.ghost/...
-apps/checkout/.ghost/...
-apps/checkout/review/page.tsx
-```
-
-For a path like `apps/checkout/review/page.tsx`, Ghost resolves each
-`/manifest.yml` from root to leaf. Each package is a
-sparse patch: it contributes only the facets it knows, and the resolved stack
-supplies the working context. The merged stack is broad-to-local:
-
-- child entries with the same `id` replace parent entries;
-- scalar summary fields use the nearest child value;
-- arrays merge with de-dupe;
-- child-relative paths normalize to repo-root paths in reports;
-- checks merge by `id`, so a child check with `status: disabled` suppresses an
- inherited active check;
-- intent situations, principles, and experience contracts merge by `id`, with
- child entries winning;
-- composition patterns, inventory exemplars, and sources merge by `id`, with
- child entries winning.
-
-Use nested packages when an area has genuinely different surface composition,
-not just because it has different files. A nested package does not need to
-restate inherited intent, inventory, composition, or validation checks.
-
-For workspace monorepos, start with a safe plan:
-
-```bash
-ghost init --monorepo
-```
-
-This creates or preserves the root `.ghost/` package, detects child package
-roots from workspace metadata, and prints proposed `ghost init --scope ...`
-commands. Add `--apply` when you want Ghost to create the detected child
-packages:
-
-```bash
-ghost init --monorepo --apply
-```
-
-## Core Commands
-
-```bash
-ghost init
-ghost scan --format json
-ghost lint .ghost
-ghost verify .ghost --root .
-ghost check --base main --format json
-ghost review --base main
-ghost emit review-command --path apps/checkout/review/page.tsx
-ghost relay gather apps/checkout/review/page.tsx --format json
-ghost relay gather --request request.yml --format json
-```
-
-`ghost scan` reports package contribution facets. Useful `intent` means any
-non-empty summary field, situation, principle, or experience contract. Useful
-`inventory` means topology scopes or surface types, curated building blocks,
-exemplars, or source links. Useful `composition` means at least one pattern.
-Useful `validate` means at least one deterministic check. Absent facets are
-reported as absent contributions, not incomplete packages.
-
-Use raw repo signals when observed repo facts are useful authoring evidence:
-
-```bash
-ghost signals .
-```
-
-Curate durable conclusions into `intent.yml`, `inventory.yml`, or
-`composition.yml`.
-
-## Authoring Rules
-
-- Write durable surface intent in `intent.yml`.
-- Write curated repo material and exemplars in `inventory.yml`.
-- Write repeatable experience patterns in `composition.yml`.
-- Write deterministic gates in `validate.yml`.
-- Prefer typed refs over prose-only cross-links.
-- Keep ids stable after review because refs and checks depend on them.
-- Let Git review approve changes to canonical fingerprint facets.
-
-Do not:
-
-- describe root-level `fingerprint.md` or direct `fingerprint.yml` as the new
- canonical package input;
-- treat cache output as canonical surface guidance;
-- promote subjective taste directly into a check without a deterministic
- detector;
-- put structural gate configuration in intent.
-
-Legacy `resources.yml`, `map.md`, `survey.json`, `patterns.yml`, direct
-`fingerprint.md`, and direct `fingerprint.yml` files may appear in older repos
-or explicit compatibility workflows. New Ghost work should target the split
-portable package under `.ghost/`.
diff --git a/docs/generation-loop.md b/docs/generation-loop.md
deleted file mode 100644
index f6047af1..00000000
--- a/docs/generation-loop.md
+++ /dev/null
@@ -1,154 +0,0 @@
-# Fingerprint Generation Loop
-
-Ghost gives UI generators and product-development agents a local, auditable
-product-surface composition fingerprint. Generation starts from checked-in
-facets; checks and review govern the result afterward.
-
-```text
-intent.yml + inventory.yml + composition.yml
- |
- v
-host agent or generator
- |
- v
-HTML / JSX / app code
- |
- v
-ghost check + ghost review
- |
- v
-deterministic gates + advisory surface-composition findings
-```
-
-## Before Generation
-
-Gather Relay JSON when a target path is known:
-
-```bash
-ghost relay gather apps/checkout/review/page.tsx --format json
-```
-
-By default, Relay uses the resolved `.ghost` fingerprint stack as its base
-runtime. A Relay config can add declared sources, request resolvers, or opt out
-of the fingerprint base entirely with `base.kind: none`.
-
-For prompt-shaped work without a clear path, the host agent should first turn
-the ask into a structured Relay request, then pass it to Ghost:
-
-```yaml
-schema: ghost.relay-request/v1
-task: generate-interface
-selectors:
- customer: subscriber
- brand: acme
- system: portal
- moment: renewal-reminder
- medium: email
- capability: billing
-```
-
-```bash
-ghost relay gather --request-stdin --format json
-```
-
-If the host framework stores Relay config outside `.ghost/relay.yml`, keep the
-same command and pass the config:
-
-```bash
-GHOST_RELAY_CONFIG=.agents/ghost/relay.yml ghost relay gather --request-stdin --format json
-ghost relay gather stacks/portal.renewal-reminder.email.yml --config .agents/ghost/relay.yml --format json
-```
-
-The second form works for `base.kind: none` configs by synthesizing a minimal
-`task: gather` Relay request from the target path.
-
-The full `ghost.relay.gather/v2` result is the agent contract. Agents should
-read `context`, `selected_context`, `targetPaths`, `source`, `stackDirs`, gaps,
-and trace fields from JSON rather than scraping the markdown preview.
-
-Use the JSON context in this order:
-
-1. Start from the selected context hits and their match reasons.
-2. Apply intent and composition hits before choosing implementation details.
-3. Inspect inventory hits as concrete anchors.
-4. Use `inventory.building_blocks` as curated material.
-5. Run `ghost signals` when raw repo observations would help find evidence.
-6. Skim active checks in `.ghost/validate.yml` so generation avoids
- deterministic failures.
-7. Treat gaps as a signal to use local evidence provisionally or inspect the
- full facet files.
-
-For quick terminal inspection, `ghost relay gather ` still prints a
-compact human preview. The preview can omit projected Relay config sources that
-are present in JSON.
-
-Raw repo signals can help orient an agent:
-
-```bash
-ghost signals .
-```
-
-Signals answer what exists now and do not count as fingerprint contribution.
-`intent.yml` captures the intent behind the surface. Curated inventory points to
-building blocks and exemplars. `composition.yml` captures the patterns that make
-the surface feel intentional.
-
-## Govern
-
-`ghost check` is deterministic:
-
-```bash
-ghost check --base main --format json
-```
-
-Without `--package`, `ghost check` groups changed files by resolved fingerprint
-stack and runs merged checks for each group. Only active checks can block.
-
-`ghost review` is advisory:
-
-```bash
-ghost review --base main
-```
-
-Advisory review packets include the current diff, the same context-hit model as
-Relay, active checks, and finding categories for fixes, intentional
-divergence, missing fingerprint grounding, experience gaps, and eval
-uncertainty.
-
-Review findings should cite the diff location, relevant fingerprint facet refs,
-relevant exemplars when useful, any active check when blocking, and a
-selected-context gap or local-evidence rationale when the fingerprint is silent.
-
-## Remediation
-
-When review flags drift from the fingerprint, the host agent chooses the
-smallest useful response:
-
-- Fix the generated or changed code.
-- Explain why a divergence is intentional.
-- Update the split fingerprint package when the user asks to change the Ghost
- fingerprint.
-
-## CI
-
-CI should run deterministic checks for UI-touching changes. Advisory review can
-attach a packet or comment, but it should not fail the build unless a finding is
-backed by an active check.
-
-```bash
-ghost check --base main
-ghost review --base main --format markdown
-```
-
-Advanced wrappers that store fingerprint packages outside `.ghost` can set
-`GHOST_PACKAGE_DIR=` on stack-aware commands. `--package `
-remains exact single-bundle mode and bypasses stack discovery. Wrappers that
-store Relay runtime config elsewhere should set `GHOST_RELAY_CONFIG` or pass
-`ghost relay gather --config `.
-
-## Legacy Cache Helpers
-
-Older Ghost bundles used `resources.yml`, `map.md`, `survey.json`,
-`patterns.yml`, and direct `fingerprint.yml` files under `.ghost/` as capture
-material. Those files are now legacy/cache source material. Promote durable
-conclusions into `intent.yml`, `inventory.yml`, and `composition.yml`.
diff --git a/docs/ghost-fleet.md b/docs/ghost-fleet.md
deleted file mode 100644
index 25aa2c9e..00000000
--- a/docs/ghost-fleet.md
+++ /dev/null
@@ -1,68 +0,0 @@
-# Ghost Fleet
-
-`ghost-fleet` is a private workspace package for read-only elevation views
-across many design-system or product-surface fingerprints. It is not part of
-the public `@anarchitecture/ghost` npm surface.
-
-Per-repo Ghost answers "is this repo using its fingerprint faithfully?" Fleet
-answers "what does this set of systems look like together?" It computes
-deterministic facts across member snapshots, then the fleet skill turns those
-facts into a world-shape narrative.
-
-## Current Shape
-
-Fleet reads a local directory of member snapshots:
-
-```text
-fleet/
- members/
- cash-web/
- map.md
- fingerprint.md
- .ghost-sync.json
- fingerprints/
- checkout.md
- dashboard/
- map.md
- fingerprint.md
- reports/
-```
-
-Each member is read-only. Fleet does not fetch, refresh, regenerate, or author
-member fingerprints. If a member is stale, refresh the source repo or snapshot
-outside fleet, then run fleet again.
-
-## CLI
-
-```bash
-ghost-fleet members
-ghost-fleet view
-ghost-fleet emit skill
-```
-
-- `members` lists loaded members and surfaces missing or malformed inputs.
-- `view` writes `fleet.md` and `fleet.json` to `/reports/`.
-- `emit skill` installs the fleet skill bundle into a host agent directory.
-
-The emitted `ghost.fleet/v1` frontmatter contains:
-
-- parent-member pairwise distances;
-- scoped fingerprint nodes and node distances;
-- track edges read from `.ghost-sync.json`;
-- groupings by platform, build system, registry, rendering, and styling.
-
-The CLI intentionally does not write the narrative. The skill fills the body
-sections `World shape`, `Cohorts`, and `Tracks` from the deterministic output.
-
-## Boundaries
-
-- Fleet consumes direct `map.md` and `fingerprint.md` snapshots for the private
- fleet workflow. That compatibility shape does not change the public
- `.ghost/` package model.
-- Fleet may read scoped overlays from `fingerprints/.md`; those are
- member snapshots, not nested package roots.
-- Clusters are a narrative projection over distances and groupings. They are
- deliberately not serialized into `ghost.fleet/v1` frontmatter.
-- The current milestone supports `members`, `view`, and `emit skill`. Separate
- temporal aggregation, refresh, and interactive browsing remain out of scope
- until a real workflow needs them.
diff --git a/docs/host-adapters.md b/docs/host-adapters.md
deleted file mode 100644
index 32f4ca91..00000000
--- a/docs/host-adapters.md
+++ /dev/null
@@ -1,139 +0,0 @@
-# Host Adapter Integration
-
-Ghost is adapter-neutral. It owns the portable fingerprint package,
-deterministic validation, stack resolution, and machine-readable packets. Host
-tools consume that fingerprint contract and own display, severity mapping, and
-review/check file formats.
-
-## Responsibilities
-
-Ghost provides:
-
-- `.ghost/` package loading and stack merging.
-- `intent.yml`, `inventory.yml`, and `composition.yml` as generation context.
-- Optional `validate.yml`.
-- `ghost signals` stdout output for raw repo observations.
-- `ghost check --format json` as the stable `ghost.check-report/v1` contract.
-- `ghost review --format json` for advisory packets grounded in the resolved
- fingerprint stack.
-- `ghost relay gather [target] --format json` as the `ghost.relay.gather/v2`
- contract for generation context. Host adapters should consume JSON fields
- such as `context`, `selected_context`, `source`, `targetPaths`, `stackDirs`,
- gaps, and trace data instead of scraping markdown.
-- `ghost relay gather --request --format json` and
- `ghost relay gather --request-stdin --format json` for prompt-shaped tasks
- where the host adapter can provide a structured `ghost.relay-request/v1`.
-- Relay configs as the execution contract for context gathering. Omitted
- `base` means `base.kind: fingerprint`; explicit `base.kind: none` lets a
- framework repo gather declared request context without a `.ghost` package.
-- `GHOST_PACKAGE_DIR=` for wrappers that store Ghost package
- roots somewhere other than `.ghost`.
-- `GHOST_RELAY_CONFIG=` for wrappers that store Relay config
- somewhere other than `.ghost/relay.yml`.
-
-Host adapters provide:
-
-- repo-specific installation workflows
-- policies for when to capture, validate, generate from, govern, or compare a
- fingerprint
-- generated review/check files in the host's native format
-- severity mapping from Ghost's `critical | serious | nit`
-- policy for when a finding blocks, comments, or remains advisory
-- normal Git review for fingerprint edits
-
-Raw repo signals are authoring evidence, not canonical inventory. The checked-in
-facet files remain the authority.
-
-## Check Flow
-
-Run deterministic checks and consume the JSON report:
-
-```bash
-ghost check --base main --format json
-```
-
-Wrappers should map severity externally. A typical mapping is:
-
-```text
-critical -> blocking
-serious -> blocking or high-confidence review finding
-nit -> advisory
-```
-
-The exact labels belong to the host.
-
-## Custom Fingerprint Directories
-
-The default package root is `.ghost`. Wrappers can use any safe relative
-package root by setting `GHOST_PACKAGE_DIR` on the Ghost process:
-
-```bash
-GHOST_PACKAGE_DIR=.design/memory ghost init --scope apps/checkout
-GHOST_PACKAGE_DIR=.design/memory ghost stack apps/checkout/review/page.tsx --format json
-GHOST_PACKAGE_DIR=.design/memory ghost relay gather apps/checkout/review/page.tsx --format json
-GHOST_PACKAGE_DIR=.design/memory ghost check --base main --format json
-GHOST_PACKAGE_DIR=.design/memory ghost review --base main --format json
-```
-
-`--package ` remains exact single-bundle mode. Use it when the caller
-already knows the package root and wants to bypass stack discovery.
-
-## Relay Context Flow
-
-Use JSON as the agent contract:
-
-```bash
-ghost relay gather apps/checkout/review/page.tsx --format json
-```
-
-Relay first loads config by precedence: `--config`, `GHOST_RELAY_CONFIG`,
-discovered `.ghost/relay.yml`, then the built-in default config. Source paths
-inside a Relay config are resolved from the repo root/current working directory,
-not from the config file directory.
-
-When the user prompt is not naturally tied to one repo path, the host adapter
-should create a Relay request from the prompt and pass it to Ghost:
-
-```yaml
-schema: ghost.relay-request/v1
-task: generate-interface
-selectors:
- customer: subscriber
- brand: acme
- system: portal
- moment: renewal-reminder
- medium: email
- capability: billing
-```
-
-```bash
-ghost relay gather --request-stdin --format json
-```
-
-Framework-owned contexts can keep the same command and provide their runtime
-config explicitly:
-
-```bash
-GHOST_RELAY_CONFIG=.agents/ghost/relay.yml ghost relay gather --request-stdin --format json
-ghost relay gather stacks/portal.renewal-reminder.email.yml --config .agents/ghost/relay.yml --format json
-```
-
-Use `base.kind: none` in that config when there is no base Ghost fingerprint
-package. Ghost will return deterministic gaps such as `no-base-fingerprint`
-instead of throwing a missing `.ghost/manifest.yml` error.
-
-The nested `context.schema` value is `ghost.relay-context/v1`. The top-level
-`brief` field is display text for humans and compatibility. Plain markdown
-output from `ghost relay gather ` is a compact human preview and may
-omit projected Relay config sources that are present in JSON.
-
-Ghost resolves request selectors deterministically against declared Relay
-config resolvers. Natural-language extraction belongs to the host adapter, not
-Ghost core.
-
-## Fingerprint Edits
-
-Adapters do not need a special Ghost draft state. If fingerprint work is
-uncommitted or unmerged, it is draft work. Once the split fingerprint package,
-checks, decisions, or intent are checked in, Ghost treats them as truth for
-deterministic tooling.
diff --git a/docs/ideas/README.md b/docs/ideas/README.md
deleted file mode 100644
index 6f1dd212..00000000
--- a/docs/ideas/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Ideas
-
-This folder is for live, non-authoritative exploration that should not be lost
-to chat history but is not ready to become public docs or a changeset.
-
-Current public docs live one level up:
-
-- [Portable fingerprint format](../fingerprint-format.md)
-- [Generation loop](../generation-loop.md)
-- [Host adapter integration](../host-adapters.md)
-- [Ghost Fleet](../ghost-fleet.md)
-
-Retained notes:
-
-- `fingerprint-first-architecture.md` records the settled product center:
- Ghost is fingerprint-first, and drift is one governance workflow over the
- portable `.ghost/` package.
-- `ghost-ui.md` explores additive registry metadata for the private Ghost UI
- reference package.
-- `guided-migration.md` explores a future host-agent workflow for migrating one
- fingerprint toward another.
-
-Conventions:
-
-- One file per idea, kebab-case slug.
-- Add frontmatter with `status: exploring`, `status: deferred`, or
- `status: settled`.
-- Keep idea notes explicitly subordinate to the current fingerprint package
- model.
-- Delete notes that only describe superseded package splits, removed commands,
- or dead migration plans after their useful decisions are folded into current
- docs.
diff --git a/docs/ideas/fingerprint-first-architecture.md b/docs/ideas/fingerprint-first-architecture.md
deleted file mode 100644
index c49f0e50..00000000
--- a/docs/ideas/fingerprint-first-architecture.md
+++ /dev/null
@@ -1,128 +0,0 @@
----
-status: settled
----
-
-# Fingerprint-first architecture
-
-## Thesis
-
-Ghost is fingerprint-first.
-
-The durable Ghost artifact is the checked-in fingerprint package: a portable
-surface-composition contract that humans can approve and agents can act from.
-Everything else in Ghost is tooling for that contract or tooling around it.
-Drift remains important, but it is one governance workflow over the fingerprint,
-not the center of the architecture.
-
-This settles the mental model for follow-on work. Current docs describe
-`.ghost/` as the portable package and generation context.
-Superseded decomposition notes have been pruned; future idea docs should stay
-subordinate to this hierarchy. The fingerprint owns surface context. Tools
-consume, validate, compare, generate from, or govern that context.
-
-## Decisions
-
-- The fingerprint package is the durable artifact. `.ghost/` remains the
- portable boundary, anchored by `manifest.yml`.
-- Core generation input is `intent.yml`, `inventory.yml`, and
- `composition.yml`. `intent.yml` captures the intent behind the surface,
- `inventory.yml` points to curated material and exemplars, and
- `composition.yml` captures the patterns that make it feel intentional.
-- Supporting material stays subordinate to the facets:
- `validate.yml` validates deterministic obligations.
-- Tools are consumers or authors of the fingerprint. They may capture,
- validate, apply, govern, or compare the contract, but they do not replace it.
-- Drift is governance. `check`, `review`, `ack`, `track`, and `diverge` answer
- whether a change remains faithful to the fingerprint, how intentional
- divergence is recorded, and how references shift over time.
-- Git remains the approval boundary. Uncommitted or unmerged fingerprint edits
- are draft work; checked-in fingerprint files are canonical Ghost context.
-
-## Rationale
-
-Agents need surface-composition context before they generate, not only after a
-diff exists. Post-hoc drift review can catch violations, but it cannot be the
-only way Ghost participates in the work. The highest-leverage artifact is the
-structured input that lets a host agent build, revise, or explain a product
-surface while preserving hierarchy, density, restraint, copy, behavior, trust,
-and flow.
-
-That makes the fingerprint more like an upstream contract than a static
-guideline or a downstream inspection report. Review, linting, comparison,
-Relay briefs, authoring tools, marketing workflows, and vibe-coding
-workflows can all use the same checked-in surface context. Their outputs differ,
-but their source of truth is the fingerprint.
-
-This also keeps Ghost adapter-neutral. Codex, Claude, Cursor, Goose, CI
-wrappers, docs tools, and design-system integrations can bring their own
-display, policy, and execution format. Ghost owns the portable context, stable
-packets, deterministic validation, and stack resolution.
-
-## Lifecycle taxonomy
-
-Future docs, CLI help, skills, and examples should organize Ghost around the
-fingerprint lifecycle:
-
-| Lifecycle | Commands and consumers | Purpose |
-| --- | --- | --- |
-| Capture | `init`, `signals`, `scan` | Create the package, inspect repo signals, and report contribution facets. |
-| Validate | `lint`, `verify` | Check schema shape, refs, evidence, exemplars, and deterministic package quality. |
-| Generate / apply | `relay gather`, host agents, future generation consumers | Give agents the upstream surface-composition contract before they build or revise. |
-| Govern | `check`, `review`, `ack`, `track`, `diverge` | Validate changed surfaces, produce advisory findings, and record stance toward divergence. |
-| Compare | `compare`, fleet-style analysis | Understand distance, cohorts, reference relationships, and change across packages. |
-
-The taxonomy is conceptual. This memo does not rename commands, change schemas,
-or create public API guarantees. It gives follow-on work a shared hierarchy.
-
-## Consequences
-
-- Positioning should lead with the portable surface-composition fingerprint, not
- drift detection. A good default sentence is: Ghost captures the composition of
- a product surface: the intent behind it, the materials it draws from, and the
- patterns that make it feel intentional.
-- Docs IA should make the lifecycle visible. Drift/review docs remain useful,
- but they should live under governance rather than define the product.
-- CLI help should group commands by fingerprint lifecycle where practical.
- Existing commands can stay stable while descriptions and ordering teach the
- new mental model.
-- Skill references should lead with recall, briefing, and generation from
- fingerprint facets before review, verification, and remediation workflows.
-- Schema exploration may investigate richer fingerprint concepts such as
- signature moves, memorable product gestures, and executable or buildable
- inventory evidence. Those ideas are follow-on design work, not new schema in
- this memo.
-- Dogfooding should update Ghost's own fingerprint and examples to express this
- framing. The repo should be able to describe itself as fingerprint-first
- before it asks other repos to do the same.
-
-## Follow-on tracks
-
-1. Positioning and docs language: update README, package README, and core docs
- so new adopters meet Ghost as a fingerprint system.
-2. Docs information architecture: organize docs pages around capture, validate,
- generate/apply, govern, and compare.
-3. CLI taxonomy: review command help, docs-generated CLI reference, and command
- ordering for lifecycle clarity without changing behavior.
-4. Skill workflow: revise the installed Ghost skill bundle so agents brief and
- generate from fingerprints before reviewing drift.
-5. Schema and model exploration: write separate design notes for signature
- moves and buildable inventory evidence before any schema change.
-6. Dogfood fingerprint: update `.ghost/` for this repo and validate
- it with `ghost scan`, `ghost lint`, and `ghost verify`.
-
-## Interface stance
-
-This memo implements no public CLI, schema, package, export, or wire-format
-change. Any future interface change should be proposed in its own plan and
-linked back to this memo as rationale.
-
-## Read-back checks
-
-The memo is successful if:
-
-- A new adopter can understand Ghost without assuming it is primarily a drift
- tool.
-- A contributor can see why drift commands still matter.
-- A follow-on agent can split docs, CLI, skill, schema, and dogfood work without
- asking what the architectural center is.
-- Nothing here contradicts the current canonical package shape under `.ghost/`.
diff --git a/docs/ideas/ghost-ui.md b/docs/ideas/ghost-ui.md
deleted file mode 100644
index da0a7b71..00000000
--- a/docs/ideas/ghost-ui.md
+++ /dev/null
@@ -1,120 +0,0 @@
----
-status: exploring
----
-
-# ghost ui
-
-> Fingerprint-first context: ghost-ui is reference inventory and evidence for
-> fingerprints. It can improve generation and governance, but it is not the
-> portable surface-composition contract itself. This is exploratory registry
-> metadata, not a public CLI or package split plan.
-
-## Why it's interesting
-
-ghost-ui is the canonical registry witness. Ghost needs to work across Cash iOS
-(Bazel, SwiftUI), Cash Android (Gradle, Compose), and arbitrary one-off web
-repos, but ghost-ui can lean all the way into the shadcn registry convention and
-prove the loop end-to-end. When the registry case works perfectly somewhere, it
-should work here.
-
-The opportunity is **registry.json**. Every shadcn registry has it. It already lists components, files, and dependencies. With minimal extension it becomes useful implementation vocabulary for component-level metadata — and ghost-ui is the place to demonstrate that without coupling other tools to it.
-
-The contract: Ghost workflows can opportunistically light up component-level
-features when `registry.json` is present and well tended. They never require it.
-Tools see registries and filesystems; ghost-ui makes the registry case sing.
-
-## What ghost-ui adds on top of "shadcn registry library"
-
-A three-part demonstration:
-
-1. **Stays a shadcn registry package.** The registry extension is additive and
- does not change the public `.ghost/` package model.
-2. **Per-component dimension tags.** Each component in registry.json can carry optional `meta.fingerprint_dimensions: [palette, spacing, typography, surfaces]` declaring which design dimensions the component primarily expresses. Review and comparison workflows can use this for higher-confidence attribution.
-3. **Shape-aware exemplar tags.** Examples can distinguish atoms from composed response shapes with optional `meta.exemplar_kind: "atom" | "shape"` and `meta.response_shapes: ["article" | "tracker" | "comparison" | "card"]`. This gives generators a narrow reference set before they compose a freeform answer.
-
-All parts are optional from the tool side. Tools degrade gracefully when they're absent. ghost-ui is the place the registry case is maximally present.
-
-## Registry.json extension — opportunistic, not breaking
-
-Today's registry.json is shadcn-shaped. The extension is purely additive metadata under a namespaced key:
-
-```json
-{
- "name": "ghost-ui",
- "homepage": "...",
- "items": [
- {
- "name": "button",
- "type": "registry:ui",
- "files": ["components/ui/button.tsx"],
- "meta": {
- "fingerprint_dimensions": ["palette", "surfaces", "typography"]
- }
- }
- ]
-}
-```
-
-The shadcn schema doesn't care about `meta`. Other registries (boss-ui, future shadcn variants) can adopt the convention without coordination — same way `keywords` works in `package.json`.
-
-If `meta.fingerprint_dimensions` is missing, drift attributes to the component but not to specific dimensions. Pure progressive enhancement.
-
-For exemplar metadata, `atom` means a primitive control such as badge, button, cell, or input. `shape` means a composed output: article for plans/timelines/worksheets, tracker for metrics/progress/reviews, comparison for tradeoffs/options, and card for compact focused recommendations. Card is one shape, not the default layout for every generated answer.
-
-## CI loop
-
-Ghost UI should exercise the loop through its checked-in fingerprint package and
-registry source. Workflows that compare or review another product against
-ghost-ui should own their own reports; `packages/ghost-ui` remains the observed
-reference target.
-
-## Component-level dimension provenance
-
-Each component declares which design dimensions it expresses. `Button` is primarily palette + radius + type-scale. `Card` is primarily surface + spacing. `Tooltip` is primarily contrast + animation.
-
-Why bother: without dimension tags, review and comparison have to infer
-attribution from file content alone. With them, attribution becomes
-deterministic: a palette shift in `Button.tsx` is a strong signal because Button
-is declared to be palette-sensitive.
-
-Implementation: prefer explicit registry metadata. A source comment such as
-`// @ghost-dimensions palette,radius` can be a convenience input, but the
-registry remains the durable reference.
-
-## Cross-tool implications
-
-| Workflow | When ghost-ui's conventions are present |
-|---|---|
-| **Fingerprint authoring** | The agent can read per-component dimension tags as evidence for which dimensions the design language emphasizes. |
-| **Review and comparison** | Component-level attribution becomes higher confidence when registry metadata identifies the dimensions a component carries. |
-| **Fleet analysis** | Registry-aware cohorts can be compared more precisely when members expose the same metadata convention. |
-
-None of these *require* the convention. All of them benefit when it's present.
-
-## ghost-mcp implications
-
-The existing `ghost-mcp` server (under `packages/ghost-ui/src/mcp/`) re-exposes the registry to AI assistants. With the conventions above, the MCP can also re-expose:
-
-- Per-component dimension tags as tool output.
-
-This is the natural extension: the MCP becomes a focused registry server. For
-example, "give me the Button component plus the dimensions it carries."
-
-## Open questions
-
-- **Dimension vocabulary.** Current fingerprint dimensions are palette / spacing / typography / surfaces. Are those the right tags for components? Probably yes, but registry metadata should stay useful even when no package-local fingerprint is present.
-- **Comment sentinel format.** `// @ghost-dimensions palette,radius` is one option. JSDoc tag is another. Probably JSDoc tag for compatibility with existing component doc conventions.
-- **CI fail vs report.** Should a Ghost UI scan workflow block on drift, or just report? Lean: block on artifact lint where that workflow owns artifacts, advisory on drift (report on PR comment, don't fail). Drift signals; humans decide.
-
-## Out of scope
-
-- Coupling other tools to ghost-ui specifically. Anything that requires `meta.fingerprint_dimensions` is wrong; everything that benefits from it when present is right.
-- Republishing ghost-ui to npm. Stays registry-distributed via shadcn `add`.
-- Renaming or restructuring the existing 97-component library. The conventions are additive metadata, not refactors.
-- A "Ghost-flavored shadcn" registry format. The whole point is that shadcn's registry is enough — Ghost just reads it well.
-
-## Next steps
-
-1. Pick 5–10 components, add `meta.fingerprint_dimensions` to each. Validate the dimension vocabulary feels right.
-2. Tag the example catalogue as atoms vs shapes where examples are composed outputs, then validate whether agents select narrower references.
-3. Document the convention in the package README so other registries can adopt — this is the part that turns ghost-ui from "our reference" into "the reference."
diff --git a/docs/ideas/guided-migration.md b/docs/ideas/guided-migration.md
deleted file mode 100644
index 4355181c..00000000
--- a/docs/ideas/guided-migration.md
+++ /dev/null
@@ -1,48 +0,0 @@
----
-status: exploring
----
-
-# Guided migration: drifting a fingerprint toward another
-
-> Fingerprint-first context: migration treats distance as a governance signal
-> between two fingerprints. The migration target is another approved contract,
-> not drift for its own sake.
-
-An agentic loop where repo A consciously migrates toward repo B's visual direction, driven by fingerprint distance + vector as the signal.
-
-## The observation
-
-Embedding distance is a *tier selector*, not a progress bar. Closing 0.6 → 0.3 is usually easier than 0.3 → 0.05: the first half removes obviously wrong answers (different font family, different base unit, different radii language); the second half means reconciling deliberate choices that both sides consider correct. The last 0.05 is where local surface character lives and often shouldn't go to zero at all.
-
-## Distance tiers
-
-| Distance | Meaning | Mode |
-|---|---|---|
-| < 0.15 | Accidental drift | **Reconcile** — token renames, no philosophy work |
-| 0.15 – 0.3 | Deliberate variance on shared foundation | **Negotiate** — diff decisions, `track` or `diverge` each gap |
-| 0.3 – 0.5 | Different decisions on similar kind of system | **Track decisions first, tokens follow** — chasing the scalar alone will mimic without migrating |
-| > 0.5 | Different design languages | **Question the premise** — often not a migration |
-
-## What the scalar hides
-
-- **Shape.** Distance 0.3 spread across four dims is uniform drift; 0.3 concentrated in palette is a single PR. Same headline, opposite plans. `computeDriftVectors` already exposes the per-dim direction.
-- **Irreducible distance.** Some gap is intentional (accessibility floor, CJK glyph support, dense-data use case). Target isn't `d = 0`; it's `d ≤ floor + ε` where `floor = Σ(diverged_dims × weight)`. Floor is computed from the manifest, not guessed.
-
-## What would be built
-
-A `migrate.md` skill recipe alongside profile / review / verify / generate / discover. Loop shape:
-
-1. `track B` — snapshot starting per-dim distances into `.ghost-sync.json`.
-2. Pick the steepest dim from `computeDriftVectors`.
-3. Host agent translates that delta into code edits, respecting the tier (reconcile vs negotiate vs track).
-4. Re-profile → `ack` → repeat.
-5. Stop when `d ≤ floor + ε`, or agent hits a dim the user marks `diverging`.
-
-No CLI primitive is missing. All the interpretation work lives in the recipe.
-
-## Open questions
-
-- **Ordering within a tier.** Vector-first (fast, risks mimicry), decisions-first (correct, slow), or interleaved (each `ack` commits one dim + the decisions that justify it). Current lean: interleaved.
-- **Detecting when the scalar is lying.** A local fingerprint can descend the vector gradient without importing decisions, landing at low `d` but not actually looking like B. Candidate: don't declare success until *both* the machine dims and the decisions dim are inside tolerance.
-- **Diverge budget up front.** Should `track` accept `--diverge ,` so the floor is known before the loop runs, instead of discovered mid-migration?
-- **Symmetry.** `checkBounds` already flags `reconverging` when a diverging dim has closed to < 50% of acked distance. Guided migration is the deliberate form of that — same bookkeeping, inverted intent. Worth thinking about whether the two should share a verb.
diff --git a/docs/language-fingerprints.md b/docs/language-fingerprints.md
deleted file mode 100644
index 026c1f30..00000000
--- a/docs/language-fingerprints.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# Language In The Fingerprint
-
-Voice and language are part of a product surface. Copy drifts the same way
-layout drifts: generated notifications, error messages, empty states, and
-button labels slowly stop sounding like one product.
-
-Ghost does not need a new domain, schema, or dimension set to capture this.
-Language flows through the same facets as every other
-surface-composition concern:
-
-- `intent.yml` carries voice intent.
-- `inventory.yml` points at copy material and external writing
- standards.
-- `composition.yml` carries copy patterns.
-- `validate.yml` carries the deterministic subset.
-
-This document shows the mapping. Nothing here changes the
-`ghost.fingerprint/v1` schema.
-
-## Voice Intent Lives In Intent
-
-`intent.summary.tone` already exists for tone words. Voice rules that need
-rationale become principles. Surfaces with non-negotiable wording become
-experience contracts.
-
-```yaml
-# intent.yml
-summary:
- tone:
- - plain
- - direct
- - warm
-principles:
- - id: action-first-copy
- principle: Copy leads with what the reader can do next, not with what the system did.
- applies_to:
- paths:
- - src/i18n/**
- - src/components/**
- surface_types:
- - error-state
- - empty-state
- guidance:
- - Prefer "Try again" over "The operation failed."
- - Keep apology framing out of routine errors.
- evidence:
- - path: src/components/error-banner.tsx
- note: Existing errors already lead with the recovery action.
-experience_contracts:
- - id: exact-wording-surfaces
- contract: Designated surfaces (legal text, consent prompts, destructive confirmations) use approved exact wording and are never paraphrased.
- applies_to:
- surface_types:
- - legal-text
- - consent-prompt
- - destructive-confirmation
- obligations:
- - Treat the approved string as the source of truth, not a style suggestion.
- - Route wording changes through ordinary Git review.
- check_refs:
- - validate.check:no-banned-phrases
-```
-
-Scope every voice entry with `applies_to`. Selective context assembly uses
-`applies_to` paths, scopes, and surface types to decide which fingerprint
-entries reach an agent for a given piece of work. Scoped voice entries are
-selected when copy work touches their surfaces and stay out of the way when
-it does not; unscoped entries only surface through ref edges or the global
-fallback.
-
-## Copy Material Lives In Inventory
-
-Most teams already maintain writing standards somewhere: a style guide, a
-terminology list, a banned-phrase list. The fingerprint should point at that
-source, not fork it. `inventory.sources` already supports this.
-
-```yaml
-# inventory.yml
-building_blocks:
- files:
- - src/i18n/en.json
- notes:
- - User-facing strings are centralized in the i18n catalog; copy edits happen there.
-sources:
- - id: writing-standards
- kind: url
- ref: https://example.com/your-org/writing-standards
- note: Org voice rules and terminology. Normativity levels map to Ghost per the table in docs/language-fingerprints.md.
-exemplars:
- - id: refund-error-copy
- path: src/components/refund-error.tsx
- surface_type: error-state
- why: Canonical error shape - states what happened, then the next step, in under two sentences.
-```
-
-External standards stay maintained in one place. The fingerprint records which
-source applies and which surfaces it governs.
-
-## Copy Patterns Live In Composition
-
-`composition.yml` already has `kind: content` for exactly this.
-
-```yaml
-# composition.yml
-patterns:
- - id: error-message-shape
- kind: content
- pattern: Error copy states what happened, then the next step the reader can take.
- applies_to:
- surface_types:
- - error-state
- guidance:
- - Two sentences maximum for inline errors.
- - Name the recovery action in the same breath as the failure.
- anti_patterns:
- - Passive apology framing ("we're sorry to inform you").
- - Blaming the reader ("you entered an invalid value").
- check_refs:
- - validate.check:no-banned-phrases
- evidence:
- - path: src/components/error-banner.tsx
- - id: button-labels-are-verbs
- kind: content
- pattern: Buttons label the action the reader takes, as a verb phrase.
- anti_patterns:
- - Generic labels ("OK", "Submit") where a specific verb exists.
-```
-
-## The Deterministic Subset Becomes Checks
-
-Writing standards commonly carry normativity levels, in the spirit of
-RFC 2119: some rules are absolute, some are recommended, some are contextual.
-Map them onto the existing check lifecycle instead of inventing a new one:
-
-| Standard's level | Ghost expression | Effect |
-| --- | --- | --- |
-| must (legal wording, banned phrases) | `validate.yml` entry with `status: active` | `ghost check` can fail the diff |
-| should (strong recommendation) | `validate.yml` entry with `status: proposed` | Surfaces in `ghost review` as advisory |
-| may / contextual | `composition.yml` guidance only | Generation input, never a gate |
-
-Only the mechanically detectable subset belongs in `validate.yml`. The
-`forbidden-regex` and `required-regex` detectors cover banned phrases and
-required boilerplate today:
-
-```yaml
-# validate.yml
-checks:
- - id: no-banned-phrases
- title: Banned phrases stay out of user-facing copy
- status: active
- severity: serious
- derivation:
- intent:
- - intent.experience_contract:exact-wording-surfaces
- composition:
- - composition.pattern:error-message-shape
- applies_to:
- paths:
- - src/i18n/**
- - src/components/**
- detector:
- type: forbidden-regex
- pattern: "we'?re sorry to inform you|per our policy"
- repair: Lead with the recovery action; see the error-message-shape pattern and the linked writing standards.
-```
-
-Everything that needs reading comprehension - tone, register, audience fit -
-stays advisory. `ghost review` routes it to the host agent with the relevant
-intent and composition refs; it never blocks on its own.
-
-## What Ghost Deliberately Does Not Do
-
-- Ghost does not ship a voice ontology, tone scales, or scored language
- dimensions. Voice rules are curated intent, owned by the team that writes
- them, approved through Git review like every other fingerprint edit.
-- Ghost does not embed any organization's style guide. The fingerprint points
- at one through `inventory.sources`.
-- Advisory copy critique never gates CI. Only active deterministic checks
- block, exactly as with visual and structural drift.
-
-## Workflow
-
-Capturing language follows the same loop as any other facet content: inventory
-the user-facing strings, read the standards source the inventory declares,
-draft the smallest evidence-backed entries, and ask a human to curate the
-claims. The `voice` recipe in the Ghost skill bundle walks an agent through it.
diff --git a/docs/purposes.md b/docs/purposes.md
new file mode 100644
index 00000000..468f03d6
--- /dev/null
+++ b/docs/purposes.md
@@ -0,0 +1,122 @@
+# Architecture: What Fingerprints Are For
+
+> **Audience: Ghost maintainers and contributors.** This is the internal model
+> doc. It defends the boundary between the fingerprint artifact and the consumers
+> that read it. It is not an onboarding guide and it assumes the full vocabulary
+> (projection, leak, corpus, glossary). If you are adopting Ghost, start with
+> [Five-Minute Ghost](../apps/docs/src/content/docs/quickstart.mdx) and
+> [Getting Started](../apps/docs/src/content/docs/getting-started.mdx) instead.
+
+Ghost has one artifact, the `.ghost/` fingerprint package, and several consumers
+that read it. This page exists to keep them honest.
+
+## The rule
+
+> A consumer may read the fingerprint through any **projection** it likes. A
+> consumer may **not** change the shape of the fingerprint to suit itself.
+
+The fingerprint is a deliberately dumb source of truth. It does not know who is
+asking. Every purpose lives in the projection, not in hidden routing logic.
+
+The test for any feature that "feels bundled":
+
+> Does serving this purpose require changing the *shape* of the fingerprint: the
+> flat corpus, node frontmatter, filename-kind convention, checks reservation, or
+> glossary?
+> - **No** then it is a projection. Fine. Keep it out of the model.
+> - **Yes** then that is a leak. Write it down below and fix the boundary.
+
+## The model
+
+The package is a **flat corpus of prose nodes**. A node is a markdown file, its
+id is its filename with `.md` dropped (`principle.density.md` is
+`principle.density`), its kind is the filename prefix before the first dot, and a
+bare name (`voice.md`) is uncategorized. The glossary declares the category
+vocabulary. There is no graph: no hierarchy, no inheritance, no edges. Nesting
+into folders is a browsing convenience only.
+
+| Part | Job |
+| --- | --- |
+| `manifest.yml` | Schema version and package id; the package's anchor. |
+| `glossary.md` | The author's dictionary: every term with defined meaning in the corpus. Ghost ships no fixed vocabulary. |
+| Prose nodes (`..md`, `.md`) | Durable brand truths; each body answers why (the stance), with what (the materials), or how it is assembled (the patterns). Altitude lives in prose; narrower truths name their condition. |
+| Node frontmatter | `description` (retrieval payload) and optional `materials` (repo-relative paths/globs or HTTPS URLs for concrete materials the prose governs). |
+| `checks/` | Optional review assertions binding to nodes with `references`. Never a node source and never generation input. |
+
+One resolution mechanism, read-only:
+
+- **The menu.** `ghost gather` emits every node's id, kind, description, and
+ material count. The agent reads the ask against descriptions and pulls the
+ truths it judges relevant. Ghost does no NLP and no selection.
+
+The entrypoint node is `index.md` (id `index`): the human-curated front door. It
+is an ordinary node, listed in the menu like any other.
+
+Checks are **not** nodes and are **never gathered**. They live in `checks/`,
+bind to the prose they enforce via `references`, and are consumed only by
+feedback projections such as `ghost review`.
+
+Two rules keep the reservation honest:
+
+- **The reserved list is closed.** `manifest.yml`, `glossary.md`, `checks/`.
+ A new root entry ships only with an intentional schema change.
+- **Materials are locators, not guidance.** Components, tokens, logos,
+ illustrations, motion files, code patterns, and external asset libraries all
+ use `materials`. The meaning of those materials lives in the node prose.
+
+## The consumers
+
+| Consumer | CLI surface | Projection it needs | Reads | Changes the model? |
+| --- | --- | --- | --- | --- |
+| **Authoring** | `ghost init`, `ghost validate`, `ghost checks init` | The raw nodes, checks, and glossary for a human or agent writing the fingerprint. | the package | **No**, this is the model. |
+| **Generation** | `ghost gather [ask…]`, `ghost pull ` | The flat menu, then selected node bodies and materials. | nodes only | **No** if selection stays with the agent and checks stay invisible. |
+| **Local signal** | `ghost pulse` | The gitignored event tape (`.ghost/.events`) written by `gather` and `pull`, used to tune descriptions and menu ergonomics. | event ids and miss suggestions | **No**, observability must not become ranking, memory, or canonical state. |
+| **Diff review** | `ghost review` | Touched files matched to node `materials`, relevant checks, referenced prose, gaps, and the diff. | nodes, checks, diff | **No** if checks bind by `references` and are not gathered. |
+| **Fleet** | (future) | Many fingerprints at once: distances, cohorts, summaries. | many corpora, read-only | **No**, consumes exports read-only. |
+
+## Known leaks
+
+1. **Retrieval needs pushed into the shape.** When selection feels imprecise, the
+ temptation is to encode routing in data: proliferating filename kinds until
+ they become destinations, or turning the glossary into a dispatch table.
+ *Fix: `description` is the retrieval payload; sharpen descriptions, show the
+ menu, let the agent pick.*
+
+2. **Filing by destination.** A truth authored as `for-emails.md` smuggles a
+ routing model into the corpus. Conditions belong in prose as situations, not
+ filename buckets.
+
+3. **Guidance smuggled into `materials`.** A material locator list that starts
+ carrying roles, rules, or semantic metadata becomes a second schema.
+ *Fix: keep `materials` as strings only; write meaning in the node body.*
+
+4. **Checks becoming generation input.** Checks are feedback assertions. If they
+ appear in `gather`, the model starts writing to the test and the review
+ signal collapses.
+ *Fix: `checks/` is reserved and never loaded as nodes.*
+
+5. **Checks routing by anything other than `references`.** Filtering checks by
+ kind, folder, or implicit corpus convention leaks governance policy into the
+ artifact.
+ *Fix: a check says what prose it enforces via explicit node references.*
+
+6. **A consumer demanding structure back.** Any consumer that wants edges,
+ hierarchy, inheritance, or a precomputed slice is asking the corpus to do the
+ projection's job.
+ *Fix: the consumer builds its projection at read time; the corpus stays flat.*
+
+## What we are NOT doing
+
+- **Not** reintroducing a graph: no hierarchy, inheritance, or cross-node edges.
+- **Not** adding a selection engine inside the artifact; the agent selects
+ against descriptions.
+- **Not** letting checks become generation input.
+- **Not** letting `materials` become an asset metadata schema.
+- **Not** giving any consumer write access to the shape of the corpus.
+
+## One line
+
+The flat corpus is how brand truths are **stored and owned**; the glossary plus
+the `gather` menu is how context is **selected**; checks are how review is
+**grounded**. One model, many projections, and the model never bends to serve a
+projection.
diff --git a/docs/relay-configs-and-context.md b/docs/relay-configs-and-context.md
deleted file mode 100644
index 19a87a6f..00000000
--- a/docs/relay-configs-and-context.md
+++ /dev/null
@@ -1,281 +0,0 @@
----
-title: Relay configs and context
-description: How Ghost Relay gathers structured context and lets repos add declared sources.
----
-
-# Relay configs and context
-
-`ghost relay gather` is the single command agents call to gather context. Relay
-loads a Relay config, the config chooses the base runtime, and Ghost emits the
-same `ghost.relay.gather/v2` JSON contract for agents. Markdown output remains a
-compact human preview.
-
-```text
-ghost relay gather
--> load Relay config
--> choose base runtime
--> resolve target/request context
--> emit ghost.relay.gather/v2 JSON
-```
-
-The default OSS shape remains the flat `.ghost/` package:
-
-```text
-.ghost/
- manifest.yml
- intent.yml
- inventory.yml
- composition.yml
- validate.yml
-```
-
-That shape is the default runtime. A repo only needs a Relay config when it
-wants extra project-owned context files, such as product questions, source
-references, brand guidance, or declared request resolvers, to appear in Relay
-JSON. Agent-framework repos can also provide a Relay config from another
-location and opt out of a base fingerprint package.
-
-## Relay Configs
-
-A Relay config is loaded before Relay resolves context. Precedence is:
-
-1. `--config `
-2. `GHOST_RELAY_CONFIG`
-3. discovered `.ghost/relay.yml`
-4. the built-in default config
-
-OSS Ghost does not auto-discover framework-specific paths such as
-`.agents/ghost/relay.yml`. Pass those paths explicitly or set
-`GHOST_RELAY_CONFIG`.
-
-```bash
-ghost relay gather app/settings/page.tsx --config .ghost/relay.yml --format json
-GHOST_RELAY_CONFIG=.agents/ghost/relay.yml ghost relay gather --request-stdin --format json
-```
-
-Source paths and resolver globs are relative to the repo root/current working
-directory, not to the directory containing the config file.
-
-Minimal fingerprint-based example:
-
-```yaml
-schema: ghost.relay-config/v1
-id: acme.product-surface/v1
-
-base:
- kind: fingerprint
-
-sources:
- - id: product-questions
- path: product/questions.yml
- section: questions
- items: questions
- summary: question
- include:
- - blocks
- max_chars: 4000
-```
-
-Omitting `base` is the same as:
-
-```yaml
-base:
- kind: fingerprint
-```
-
-Each source says: read this file, take these items, summarize each item with
-this field, and optionally include a small set of fields as bounded content.
-Relay does not read arbitrary project files.
-
-Request-only configs opt out of the base fingerprint runtime:
-
-```yaml
-schema: ghost.relay-config/v1
-id: demo.agent-context/v1
-
-base:
- kind: none
-
-sources: []
-
-request_resolvers:
- - id: demo-stacks
- kind: stack
- files:
- - stacks/*.yml
- schema: demo.stack/v1
- unit_sources:
- - id: unit-questions
- path: "{unit}/questions.yml"
- section: questions
- items: questions
- summary: question
-```
-
-With `base.kind: none`, Relay does not load `.ghost/manifest.yml`. It resolves
-only declared config sources and request resolvers:
-
-```bash
-ghost relay gather --request-stdin --config .agents/ghost/relay.yml --format json
-ghost relay gather stacks/portal.renewal-reminder.email.yml --config .agents/ghost/relay.yml --format json
-```
-
-The second form synthesizes a minimal Relay request with `task: gather` and the
-target path, so a stack file can still be gathered through the same command.
-
-## Relay Requests
-
-Agents often start from a natural-language prompt rather than a file path. The
-host adapter should turn that prompt into a small `ghost.relay-request/v1`
-object, then ask Ghost to resolve it deterministically:
-
-```bash
-ghost relay gather --request request.yml --format json
-ghost relay gather --request-stdin --format json
-```
-
-Example request:
-
-```yaml
-schema: ghost.relay-request/v1
-task: generate-interface
-prompt: Generate the right interface for a subscriber renewal reminder in email.
-selectors:
- customer: subscriber
- brand: acme
- system: portal
- moment: renewal-reminder
- medium: email
- capability: billing
-constraints:
- output: interface
-```
-
-Ghost does not infer selectors from natural language. Codex, Claude, Goose, or
-another host harness owns that extraction. Ghost receives selectors and resolves
-declared context with exact/id-normalized matching.
-
-Relay configs can declare stack-style request resolvers:
-
-```yaml
-schema: ghost.relay-config/v1
-id: demo.product-surface/v1
-
-base:
- kind: fingerprint
-
-sources: []
-
-request_resolvers:
- - id: demo-stacks
- kind: stack
- files:
- - stacks/*.yml
- schema: demo.stack/v1
- unit_sources:
- - id: unit-questions
- path: "{unit}/questions.yml"
- section: questions
- items: questions
- summary: question
- - id: unit-sources
- path: "{unit}/sources.yml"
- section: sources
- items: sources
- summary: summary
- - id: unit-composition
- path: "{unit}/composition.yml"
- section: extra:composition
- items: patterns
- summary: pattern
-```
-
-A matching stack file can carry selector metadata:
-
-```yaml
-schema: demo.stack/v1
-id: portal.renewal-reminder.email
-title: Portal renewal reminder via email
-task_context:
- customer: subscriber
- system: systems.portal
- moment: moments.subscription-renewal-reminder
- medium: media.email
- capability: capabilities.billing
-units:
- - systems/portal
- - media/email
- - capabilities/billing
-```
-
-When a request matches exactly one stack, Relay projects the declared unit
-sources into `questions`, `sources`, and `extra:*`. Unit sources cannot project
-into canonical `intent`, `inventory`, `composition`, or `checks`; those remain
-owned by fingerprint packages unless projected as explicit extras. If selectors
-are missing, conflicting, or ambiguous, Relay records gaps and trace entries
-instead of guessing.
-
-## Sections
-
-Core sections are:
-
-- `intent`
-- `inventory`
-- `composition`
-- `checks`
-- `questions`
-- `sources`
-
-Extra sections use `extra:`, for example `extra:brand_voice`.
-
-Canonical `intent`, `inventory`, `composition`, and `checks` continue to come
-from the Ghost package schemas. Custom Relay sources initially project into
-`questions`, `sources`, and `extra:*`.
-
-## Relay Context
-
-JSON output from `ghost relay gather --format json` is the stable agent-facing
-contract:
-
-```json
-{
- "schema": "ghost.relay.gather/v2",
- "selected_context": {},
- "source": {},
- "targetPaths": [],
- "stackDirs": [],
- "brief": "# Ghost Relay Brief...",
- "context": {
- "schema": "ghost.relay-context/v1"
- }
-}
-```
-
-The nested Relay context records:
-
-- target path, request, and mode;
-- Relay config id, source, path, and `base.kind`;
-- selected section items;
-- source files for selected items;
-- suggested reads;
-- skipped context;
-- gaps and trace information.
-
-Agents and host adapters should consume JSON fields such as `context`,
-`selected_context`, `targetPaths`, `source`, `stackDirs`, gaps, and trace data.
-The top-level `brief` field is preview text for display and compatibility; do
-not scrape it as the primary agent interface. Plain markdown output may omit
-projected Relay config sources that are present in JSON.
-
-## Non-Goals
-
-- Ghost does not become a generic YAML collector.
-- OSS Ghost does not ship proprietary ontology.
-- OSS Ghost does not auto-discover `.agents` paths; use `--config` or
- `GHOST_RELAY_CONFIG`.
-- Relay does not read arbitrary files without a Relay config source.
-- Relay does not infer selectors from natural-language prompts.
-- Existing `.ghost/` packages do not need migration.
-- Relay does not summarize or interpret custom sources with an LLM.
-- Visibility is deterministic filtering and trace metadata, not an access
- control boundary.
diff --git a/docs/research-references.md b/docs/research-references.md
new file mode 100644
index 00000000..c6e40b45
--- /dev/null
+++ b/docs/research-references.md
@@ -0,0 +1,423 @@
+# Research References for Ghost's Steering Claims
+
+> **Audience: Ghost maintainers and contributors.** This doc maps the claims in
+> [Model Steering for Agent-Readable Brand Systems](./agent-readable-brand-steering.md)
+> to published research. It exists so we know which of our stances rest on
+> validated results, which are extrapolations from adjacent domains, and which
+> are our own untested priors. It is deliberately honest: a citation appears
+> here only if the finding actually supports the claim as we make it.
+
+## How to read this doc
+
+Every claim carries an evidence tier:
+
+| Tier | Meaning |
+| --- | --- |
+| **Established** | Direct, replicated, peer-visible result. The claim as written is supported. |
+| **Adjacent** | The finding is real but from a neighboring task or older model class. We are extrapolating; the extrapolation is stated. |
+| **Prior** | Mechanistically sound reasoning or practitioner consensus, but no benchmarked result for the claim as we make it. Testable; not yet tested. |
+
+Rules for maintaining this doc:
+
+- **Cite for the finding, not the aura.** A paper goes next to a claim only if
+ its actual result supports that claim. Never cite a famous paper because it
+ is nearby-sounding.
+- **Carry the caveats.** If a result is on 2021-era models, on classification
+ rather than generation, or contested, say so inline.
+- **Downgrades are contributions.** If a newer result weakens a citation here,
+ moving a claim from Established to Adjacent (or deleting the citation) is as
+ valuable as adding one.
+- **Priors want experiments.** Each Prior-tier claim should state the cheap
+ experiment that would settle it.
+
+---
+
+## 1. The retrieval gate
+
+**Ghost claim:** a truth that is not retrieved has zero effect; selection into
+context is the first steering gate (`ghost gather` / `ghost pull`, task-shaped
+descriptions).
+
+**Tier: Established.**
+
+- Lewis et al., *Retrieval-Augmented Generation for Knowledge-Intensive NLP
+ Tasks*, [arXiv:2005.11401](https://arxiv.org/abs/2005.11401). Conditioning
+ generation on retrieved context outperforms relying on parametric knowledge
+ for knowledge-intensive tasks. The foundational result behind
+ retrieve-then-generate as an architecture.
+- Park et al., *Generative Agents: Interactive Simulacra of Human Behavior*,
+ [arXiv:2304.03442](https://arxiv.org/abs/2304.03442). The closest published
+ precedent for Ghost's shape: a flat memory stream of natural-language records
+ with just-in-time relevance-scored retrieval into context. Their ablations
+ show retrieval quality dominates output believability.
+- Liu et al., *What Makes Good In-Context Examples for GPT-3?*,
+ [arXiv:2101.06804](https://arxiv.org/abs/2101.06804). Semantically
+ task-relevant in-context material outperforms randomly selected material —
+ the justification for selecting nodes against the actual ask rather than
+ shipping a static prompt.
+
+**Honesty note:** these papers validate retrieve-then-generate and
+relevance-based selection. None of them test *agent-side* selection against
+human-authored descriptions specifically (Ghost's BYOA stance). That design
+choice is architectural, argued in [purposes.md](./purposes.md), not
+literature-backed.
+
+---
+
+## 2. Relevance beats volume; small briefs
+
+**Ghost claims:** irrelevant or excess context degrades output ("3–5 nodes is
+normal; 10 is a bad selection"); more context is not more control.
+
+**Tier: Established** for degradation from irrelevant and from lengthy input.
+**Prior** for the specific 3–5 node budget.
+
+- Shi et al., *Large Language Models Can Be Easily Distracted by Irrelevant
+ Context*, [arXiv:2302.00093](https://arxiv.org/abs/2302.00093). Adding
+ irrelevant context measurably reduces accuracy — extra nodes are not merely
+ wasted, they actively hurt.
+- Levy, Jacoby & Goldberg, *Same Task, More Tokens: the Impact of Input Length
+ on the Reasoning Performance of Large Language Models*,
+ [arXiv:2402.14848](https://arxiv.org/abs/2402.14848). Reasoning degrades as
+ input grows **even when the added content is relevant**, well before the
+ context limit.
+- Hsieh et al., *RULER: What's the Real Context Size of Your Long-Context
+ Language Models?*, [arXiv:2404.06654](https://arxiv.org/abs/2404.06654).
+ Effective context is much shorter than advertised context; performance on
+ anything richer than needle retrieval decays far before the nominal window.
+
+**Honesty note:** "3–5 nodes" is our operating number, not a measured optimum.
+The literature says *smaller and more relevant is better*; it does not say
+where the knee is for brand-steering packets. Cheap experiment: same task, same
+fingerprint, pull sets of 3 / 6 / 10 nodes, blind-grade outputs for adherence.
+
+---
+
+## 3. Position and ordering in context
+
+**Ghost claims:** beginning and end of context are privileged; `ghost pull`
+emission order (stance first, guards late, skeletons dead last) and the brief's
+section order are load-bearing, not cosmetic.
+
+**Tier: Established** for position effects and order sensitivity.
+**Prior** for Ghost's *specific* ordering being optimal.
+
+- Liu et al., *Lost in the Middle: How Language Models Use Long Contexts*,
+ [arXiv:2307.03172](https://arxiv.org/abs/2307.03172). The canonical U-shaped
+ result: information at the beginning and end of context is used far better
+ than information in the middle. Directly supports placing non-negotiables
+ early and the skeleton — the thing to continue from — last.
+- Xiao et al., *Efficient Streaming Language Models with Attention Sinks*,
+ [arXiv:2309.17453](https://arxiv.org/abs/2309.17453). Mechanistic evidence
+ that initial tokens receive disproportionate attention regardless of semantic
+ content — the attention-level grounding for "beginning of context is
+ privileged."
+- Lu et al., *Fantastically Ordered Prompts and Where to Find Them*,
+ [arXiv:2104.08786](https://arxiv.org/abs/2104.08786). Identical few-shot
+ examples in different orders swing performance from near state-of-the-art to
+ near random. Ordering is a real lever. (Measured on GPT-2/GPT-3-era models in
+ classification settings; the magnitude on current instruct models is likely
+ smaller, the direction holds.)
+- Sclar et al., *Quantifying Language Models' Sensitivity to Spurious Features
+ in Prompt Design*, [arXiv:2310.11324](https://arxiv.org/abs/2310.11324).
+ Formatting choices alone cause double-digit accuracy swings. Supports the CLI
+ emitting a deterministic, consistent packet shape rather than leaving
+ assembly ad hoc per session.
+
+**Honesty note:** the literature establishes that position and order matter a
+lot. It does not establish that *Ghost's* order (stance → materials → rules →
+guards → skeletons) is the best one. That order is derived from the position
+results plus the continuation argument in §8, and it is testable: permute
+packet order on a fixed task set and grade adherence.
+
+---
+
+## 4. Materials for facts: models invent what they were never taught
+
+**Ghost claims:** models have thin, unreliable knowledge of niche APIs,
+internal design systems, and private tokens; no amount of "stay on brand" fixes
+missing facts; `materials` exists to close knowledge gaps, not to exhort.
+
+**Tier: Established.**
+
+- Kandpal et al., *Large Language Models Struggle to Learn Long-Tail
+ Knowledge*, [arXiv:2211.08411](https://arxiv.org/abs/2211.08411). Ability to
+ answer factual questions correlates strongly with how often the fact appears
+ in pretraining data. An internal token name that appeared zero times will be
+ replaced with a plausible invention. The single best citation for the
+ inventory/materials layer.
+- Meng et al., *Locating and Editing Factual Associations in GPT* (ROME),
+ [arXiv:2202.05262](https://arxiv.org/abs/2202.05262). Factual knowledge is
+ stored as localized associations in weights — grounding for "the model does
+ not know your brand as a stable object" and for why context injection, not
+ instruction, is the fix for a knowledge gap.
+- Xu et al., *Hallucination is Inevitable: An Innate Limitation of Large
+ Language Models*, [arXiv:2401.11817](https://arxiv.org/abs/2401.11817). A
+ formal, computability-theoretic argument that hallucination cannot be fully
+ trained away. Supports the structural stance that grounding plus post-hoc
+ review is necessary — you cannot prompt your way to a model that never
+ invents.
+
+---
+
+## 5. Exemplars: pattern continuation over instruction
+
+**Ghost claims:** exemplars are a strong generation-time signal; the model
+absorbs structure, rhythm, and code shape from examples and continues in that
+pattern; exemplars need annotation or the model copies accidents as brand.
+
+**Tier: Established** for demonstrations conditioning output strongly and for
+structure/format being what gets absorbed. **Adjacent** for "often the
+strongest generation-time signal" in open-ended design work.
+
+- Brown et al., *Language Models are Few-Shot Learners*,
+ [arXiv:2005.14165](https://arxiv.org/abs/2005.14165). The origin of
+ in-context demonstrations as a steering mechanism.
+- Min et al., *Rethinking the Role of Demonstrations: What Makes In-Context
+ Learning Work?*, [arXiv:2202.12837](https://arxiv.org/abs/2202.12837). In
+ classification settings, what the model uses from demonstrations is largely
+ the **format, input distribution, and label space** — the structure — more
+ than the literal input-label mapping. This is why "normative for rhythm, not
+ for the exact words" works: structure transfers regardless. It is also why
+ unannotated exemplars are dangerous — the model absorbs *everything*
+ structural, including the accidents.
+- Xie et al., *An Explanation of In-context Learning as Implicit Bayesian
+ Inference*, [arXiv:2111.02080](https://arxiv.org/abs/2111.02080), and Dai et
+ al., *Why Can GPT Learn In-Context?*,
+ [arXiv:2212.10559](https://arxiv.org/abs/2212.10559). Two mechanism accounts
+ of why demonstrations condition generation so strongly: they locate a latent
+ concept learned in pretraining / behave like implicit fine-tuning. These are
+ theoretical accounts, not settled fact, but both predict the same practical
+ behavior we rely on.
+
+**Honesty note:** the ranking claim — exemplars beating prose instructions for
+*open-ended design generation* specifically — is an extrapolation from
+classification and structured-task results. We hold it as a strong prior and
+state it as a design stance ("highest-leverage"), not a measured ordering.
+Cheap experiment: same brand truth expressed as (a) prose principle only,
+(b) prose + annotated exemplar, on a fixed generation task; blind-grade.
+
+---
+
+## 6. Anti-goals, gravity wells, and AI sameness
+
+**Ghost claims:** models regress toward the mean of training data (generic
+cards, bootstrap-blue buttons); positive adjectives do not escape the well;
+specific negation prunes it; coherence is not originality, so deliberate
+novelty must be injected by humans.
+
+**Tier: Established** for mode-seeking genericness and diversity loss.
+**Adjacent** for the negation caveat. **Prior** for "specific negation prunes
+better than positive adjectives" as stated.
+
+- Holtzman et al., *The Curious Case of Neural Text Degeneration*,
+ [arXiv:1904.09751](https://arxiv.org/abs/1904.09751).
+ Likelihood-maximization produces generic, repetitive text — the formal
+ version of the gravity well.
+- Kirk et al., *Understanding the Effects of RLHF on LLM Generalisation and
+ Diversity*, [arXiv:2310.06452](https://arxiv.org/abs/2310.06452). RLHF
+ improves generalization but measurably **reduces output diversity**. The
+ strongest citation for "AI sameness" being a property of aligned models, and
+ the direct motivation for the deliberate-novelty bucket: the system will not
+ produce non-median moves on its own.
+- Padmakumar & He, *Does Writing with Language Models Reduce Content
+ Diversity?*, [arXiv:2309.05196](https://arxiv.org/abs/2309.05196). Model
+ assistance homogenizes what *humans* produce — the downstream brand-erosion
+ argument for why steering artifacts matter at all.
+- Kassner & Schütze, *Negated and Misprimed Probes for Pretrained Language
+ Models*, [arXiv:1911.03343](https://arxiv.org/abs/1911.03343). Older LMs
+ handle negation poorly, and naming a concept — even to reject it — primes
+ it. **This is the citable basis for the guard rule in the skill bundle:
+ never state only the rejected pattern; state the positive replacement.**
+ Evidence is on BERT-era models; modern instruct models are meaningfully
+ better at negation, but "mentioning primes" remains directionally true and
+ the design rule costs nothing.
+- Si et al., *Design2Code*, [arXiv:2403.03163](https://arxiv.org/abs/2403.03163).
+ Benchmark evidence that frontend generation has systematic, measurable
+ fidelity failures against a given design — the closest published grounding
+ for the frontend gravity well specifically.
+
+**Honesty note:** we have no benchmark showing that concrete anti-goals
+("no rounded-xl card grids on gray-50") outperform positive adjectives
+("modern, clean") at escaping generic output. It follows from the diversity
+and priming results and matches consistent practitioner experience, but it is
+a Prior. Cheap experiment: same task with adjective-only vs. anti-goal-only
+steering; count generic-pattern occurrences.
+
+---
+
+## 7. Invariants: short, concrete, checkable
+
+**Ghost claim:** invariants work best when short, concrete, numeric where
+possible, and mirrored by review checks; "if it cannot be reviewed, it may not
+be an invariant yet."
+
+**Tier: Established** for verifiability as the load-bearing property.
+
+- Zhou et al., *Instruction-Following Evaluation for Large Language Models*
+ (IFEval), [arXiv:2311.07911](https://arxiv.org/abs/2311.07911). The standard
+ instruction-following benchmark is built **entirely on verifiable
+ instructions** (exact counts, always/never, checkable formats) precisely
+ because vague instructions cannot be evaluated. The same reasoning as our
+ invariant form: an unfalsifiable rule is not yet a rule.
+
+---
+
+## 8. Early commitment: skeletons and structural steering
+
+**Ghost claims:** autoregressive generation commits early; the first tokens of
+an output constrain everything after; starting from a pulled Skeleton verbatim
+controls structure better than describing structure.
+
+**Tier: Prior**, with mechanistic grounding. This is the weakest-cited claim
+in the steering doc and we should say so.
+
+- Vaswani et al., *Attention Is All You Need*,
+ [arXiv:1706.03762](https://arxiv.org/abs/1706.03762). The autoregressive
+ factorization itself: every token is conditioned on all previous tokens.
+ Early commitments constraining later ones is definitional, not empirical.
+- Ranzato et al., *Sequence Level Training with Recurrent Neural Networks*,
+ [arXiv:1511.06732](https://arxiv.org/abs/1511.06732). Exposure bias: errors
+ and choices early in a generated sequence compound, because the model
+ conditions on its own prior output.
+- Press et al., *Measuring and Narrowing the Compositionality Gap in Language
+ Models*, [arXiv:2210.03350](https://arxiv.org/abs/2210.03350). Errors
+ compound across dependent steps — indirect support for controlling the first
+ structural commitment rather than correcting downstream.
+
+**Honesty note:** "hand the model a skeleton and it stays in that structure"
+is a strong practitioner prior with mechanistic plausibility, not a
+benchmarked result for UI/design generation. Cheap experiment: fixed task,
+(a) prose structural description vs. (b) verbatim skeleton start; measure
+structural adherence. If we ever want to state this as fact in public docs,
+run that first.
+
+---
+
+## 9. Decision traces: teaching the tradeoff function
+
+**Ghost claim:** worked reasoning examples ("we chose command palette over
+drawer because…") teach the model *how the brand decides*, not just what it
+produces; models imitate reasoning traces as well as outputs.
+
+**Tier: Established** that demonstrated reasoning steers model reasoning.
+**Adjacent** for brand-tradeoff transfer specifically.
+
+- Wei et al., *Chain-of-Thought Prompting Elicits Reasoning in Large Language
+ Models*, [arXiv:2201.11903](https://arxiv.org/abs/2201.11903). Demonstrated
+ reasoning in context changes how the model reasons, not just what it
+ answers. Decision traces are brand-domain chain-of-thought exemplars.
+- Zelikman et al., *STaR: Bootstrapping Reasoning With Reasoning*,
+ [arXiv:2203.14465](https://arxiv.org/abs/2203.14465). Reasoning traces are
+ themselves effective training/steering signal — showing *how* a conclusion
+ was reached transfers better than stating the conclusion.
+
+**Honesty note:** CoT results are on math/logic/symbolic tasks. That a worked
+design tradeoff transfers the brand's *choice function* to novel conflicts is
+an extrapolation we believe but have not measured.
+
+---
+
+## 10. Checks: external review loops work; self-correction alone does not
+
+**Ghost claims:** a generate → review → repair loop is more reliable than
+perfecting the first sample; checks are advisory packets graded by an agent;
+checks must be grounded in specific fingerprint nodes.
+
+**Tier: Established**, including the caveat that makes Ghost's specific design
+necessary.
+
+- Madaan et al., *Self-Refine: Iterative Refinement with Self-Feedback*,
+ [arXiv:2303.17651](https://arxiv.org/abs/2303.17651), and Shinn et al.,
+ *Reflexion*, [arXiv:2303.11366](https://arxiv.org/abs/2303.11366).
+ Iterative feedback loops outperform single-shot generation across many
+ tasks.
+- Bai et al., *Constitutional AI: Harmlessness from AI Feedback*,
+ [arXiv:2212.08073](https://arxiv.org/abs/2212.08073). The closest
+ architectural precedent for Ghost checks: explicit written principles
+ applied at **critique time** to revise outputs, rather than stuffed into the
+ generation prompt.
+- Zheng et al., *LLM-as-a-Judge with MT-Bench and Chatbot Arena*,
+ [arXiv:2306.05685](https://arxiv.org/abs/2306.05685). Model-as-grader
+ agrees with human raters at usable rates — and has known biases (position,
+ verbosity, self-enhancement). Grounds "advisory packet for the host agent to
+ judge" and warns why check results are advisory, not authoritative.
+- Huang et al., *Large Language Models Cannot Self-Correct Reasoning Yet*,
+ [arXiv:2310.01798](https://arxiv.org/abs/2310.01798). **The caveat that
+ validates the design:** intrinsic self-correction — "review your own work"
+ with no external signal — often makes outputs *worse*. Review loops work
+ when the critique is grounded in an external criterion. This is exactly why
+ checks carry `references` to specific fingerprint nodes and concrete
+ flag-conditions, instead of asking the agent to vibe-check itself.
+
+---
+
+## 11. Checks never leak into generation
+
+**Ghost claim:** if checks appear in `gather`, the model writes to the test
+and the review signal collapses (purposes.md, leak #4).
+
+**Tier: Established** as a general principle of measurement.
+
+- Manheim & Garrabrant, *Categorizing Variants of Goodhart's Law*,
+ [arXiv:1803.04585](https://arxiv.org/abs/1803.04585). When a measure becomes
+ a target, it ceases to be a good measure — formalized into variants. The
+ feed-forward/feed-back separation in Ghost is a Goodhart firewall: checks
+ retain diagnostic value only while the generator does not optimize against
+ them.
+
+**Honesty note:** Goodhart is a principle about optimization pressure, not an
+LLM benchmark. A single generation pass that has seen a check is not
+"optimizing" in the RL sense; the leak risk compounds over repair loops and
+over authoring (checks drifting into de facto guidance). The architectural
+rule is cheap insurance justified by the principle, and we should describe it
+that way.
+
+---
+
+## Claims we deliberately do not cite
+
+Listed so nobody back-fills a weak citation later.
+
+1. **The full steering hierarchy** (examples > constraints > scaffolding >
+ persona framing > quality adjectives). Each pairwise edge has partial
+ support above, but no paper establishes the total order. It is a synthesis.
+ Public docs should say "consistent with the literature," never "shown by."
+2. **BYOA over built-in selection.** The choice to let the host agent select
+ nodes rather than ship an embedded retriever is architectural (simplicity,
+ agent context-awareness, no NLP in the CLI), defended in
+ [purposes.md](./purposes.md). The retrieval literature validates
+ retrieve-then-generate; it does not adjudicate who should do the
+ retrieving.
+3. **Flat corpus over hierarchy/graph.** No paper compares flat prose corpora
+ against hierarchical knowledge bases as LLM steering input. Our argument is
+ about authoring cost, leak prevention, and projection freedom — an
+ engineering argument, not a model-science one.
+4. **Descriptions in task language retrieve better than poetic descriptions.**
+ Almost certainly true by analogy to tool-description and retrieval-query
+ findings, but we have not seen it isolated in a study. Our own
+ `.ghost/.events` pulse data is the honest evidence source here: misses and
+ unused pulls are the experiment already running.
+5. **Vendor guidance** (Anthropic's prompt-engineering and agent-building
+ posts, OpenAI's prompting guides). Aligned with our stances on ordering,
+ examples-over-adjectives, and determinism-in-the-harness, and worth reading
+ — but they are industry practice documents, not validation, and we cite
+ them nowhere as evidence.
+
+---
+
+## Open experiments
+
+The Prior-tier claims above, collected. Each is cheap to run against a fixture
+fingerprint and a fixed task set, blind-graded:
+
+| # | Claim | Experiment |
+| --- | --- | --- |
+| 1 | 3–5 node pull budget | Pull sets of 3 / 6 / 10 nodes, grade adherence and genericness. |
+| 2 | Ghost's packet order | Permute pull emission order, grade adherence. |
+| 3 | Skeleton-verbatim start controls structure | Prose structure description vs. verbatim skeleton start, measure structural adherence. |
+| 4 | Anti-goals beat adjectives | Adjective-only vs. anti-goal-only steering, count generic-pattern occurrences. |
+| 5 | Exemplar > prose principle for design output | Same truth as prose vs. prose + annotated exemplar, blind-grade. |
+
+Results, when we have them, should be recorded here and the corresponding
+claims promoted or revised — in the steering doc as well as this one.
diff --git a/docs/vessel-agent-safe-reference-system.md b/docs/vessel-agent-safe-reference-system.md
new file mode 100644
index 00000000..c9e6c28f
--- /dev/null
+++ b/docs/vessel-agent-safe-reference-system.md
@@ -0,0 +1,182 @@
+# Vessel: Agent-Safe Reference System
+
+> **Phase 0 invariant doc.** This document sets the rules that future Vessel
+> work must preserve while reconciling latest shadcn sources, learning from
+> downstream product forks, and baking in Orbit-style LLM-safe design-system
+> discipline. It intentionally changes no runtime behavior.
+
+Vessel is Ghost's reference body: a portable, shadcn-compatible component
+registry that agents can use to compose AI/product interfaces. It should have
+taste, constraints, and safe defaults. It must not become the brand truth for
+every Ghost consumer.
+
+## North star
+
+> Vessel is an agnostic, agent-safe reference implementation. It provides a
+> coherent body a product's Ghost fingerprint can inhabit; it does not replace
+> that product's fingerprint.
+
+A consuming repo owns its product stance, flows, copy, trust obligations, and
+visual-language decisions through its local `.ghost/` fingerprint and whatever
+implementation checks it chooses to run. Vessel supplies reusable materials and
+safe authoring paths, not universal brand law.
+
+## What Vessel is
+
+- **A shadcn-compatible source-owned registry.** Components are copied into the
+ consuming repo through the shadcn registry model and may be owned there.
+- **A reference implementation.** Vessel demonstrates one coherent way to build
+ AI/product surfaces with tokens, primitives, AI elements, and interaction
+ patterns.
+- **An agent-facing system.** Registry metadata, generated skills, examples, and
+ component APIs should help agents choose named decisions instead of inventing
+ local styling.
+- **A safe default.** The baseline language should be restrained, legible,
+ workbench-like, and useful for dense AI/product interfaces.
+
+## What Vessel is not
+
+- **Not Ghost's brand model.** Brand truths live in fingerprint prose, not in
+ Vessel's registry or tokens.
+- **Not an extracted product UI kit.** Downstream product forks are useful
+ evidence and stress tests, not the visual source of truth for Vessel.
+- **Not latest shadcn with a logo.** Upstream shadcn is raw material for
+ accessibility, anatomy, and compatibility; Vessel keeps its own token contract
+ and agent-safe authoring rules.
+- **Not a sealed design system.** Consumers can own the code they install, but
+ Vessel should make the safe path obvious and the escape path visible.
+
+## Relationship to Ghost
+
+| Layer | Owns | Vessel's obligation |
+| --- | --- | --- |
+| Ghost fingerprint | Product truth: intent, stance, composition, conditions, material locators, and optional review checks | Do not encode this into Vessel as universal law. Provide materials a fingerprint can select, interpret, and review against. |
+| Vessel | Reusable implementation vocabulary: tokens, primitives, AI elements, and registry metadata | Provide coherent defaults and safe decisions without becoming product-specific. |
+
+The seam matters: Vessel can make some low-level decisions harder to get wrong;
+Ghost covers the high-altitude product truths that cannot be compiled into
+component props or lint rules.
+
+## Relationship to shadcn
+
+Vessel follows the shadcn **copy-and-own** model. Syncing with upstream shadcn is
+upstream hygiene, not visual direction.
+
+When reconciling latest shadcn components:
+
+1. **Adopt** mechanical improvements: accessibility fixes, Radix wiring, ARIA,
+ keyboard behavior, `data-slot` conventions, React compatibility, and Tailwind
+ 4-compatible structure.
+2. **Adapt** useful anatomy through Vessel's token contract and component API.
+3. **Reject** generic visual decisions that widen the authoring surface: raw
+ palette classes, arbitrary values, broad aliases, component-local theme hacks,
+ or styling that bypasses Vessel's semantic roles.
+
+Latest shadcn is a baseline to compare against. It is not authority over
+Vessel's visual language.
+
+## Relationship to downstream product forks
+
+Real product forks show what happens when a copied registry lives inside a
+shipping product and accumulates pressure. Vessel should mine that history for
+reusable discipline, not copy any product wholesale.
+
+**Take spiritually:**
+
+- one semantic authoring contract;
+- narrow product-extension tokens instead of broad alias sprawl;
+- calm, dense, legible product surfaces;
+- token checks that reject deleted families and raw palette drift;
+- generated design-system manifests and agent-facing docs;
+- real-world AI elements such as messages, tools, reasoning, code, files,
+ terminals, and prompt input.
+
+**Do not automatically copy:**
+
+- app chrome;
+- desktop-shell assumptions;
+- project-specific canvas tinting unless generalized;
+- profile/editor specifics;
+- every glass/composer surface token;
+- any brand/licensed font requirement without an explicit distribution decision;
+- product surfaces that the downstream product itself excludes as precedent.
+
+Downstream products are evidence. Vessel's job is synthesis.
+
+## Orbit lessons Vessel adopts
+
+Orbit's transferable lesson is: **make off-system output hard to express, not
+merely discouraged in prose.** For Vessel, that means:
+
+1. **Decision names beat values.** Prefer props, variants, tokens, and registry
+ metadata that name intent (`surface=card`, `tone=muted`, `density=compact`)
+ over open-ended class strings and raw values.
+2. **Docs are probability; checks are contracts.** If a rule can be deterministic
+ — no raw palette utilities, no deleted token aliases, no unapproved theme
+ bridge names — encode it as a script/lint/check.
+3. **Escape hatches must be visible.** `className`, inline `style`, arbitrary
+ values, and local forks are sometimes necessary, but they should be easy to
+ grep, count, and review.
+4. **Theme behavior belongs in tokens.** Components should consume semantic
+ roles; light/dark differences should live in the token/theme layer wherever
+ possible.
+5. **Registry metadata is part of the API.** Agents need to know when to use a
+ component, when not to, which variants are safe, and what mistakes to avoid —
+ not just its source code.
+
+## Token contract invariants
+
+Future token work should preserve this shape:
+
+```text
+primitive values
+ -> semantic roles
+ -> narrow Vessel extensions
+ -> Tailwind utility bridge
+```
+
+- Primitive values are the only broad place for literal color material.
+- Shared UI authors against semantic roles first: `background`, `foreground`,
+ `card`, `popover`, `muted`, `accent`, `primary`, `secondary`, `destructive`,
+ `border`, `input`, `ring`, and sidebar roles.
+- Vessel extensions must be narrow and job-named: composer surfaces, message
+ surfaces, tool/reasoning/status, chips, canvas, code/terminal affordances.
+- Do not reintroduce broad duplicate aliases such as `background-alt`,
+ `text-alt`, `border-strong`, or `surface-card`.
+- Bridge a token into Tailwind only when component code should author it as a
+ utility class. Raw-CSS-only hooks should remain raw CSS variables.
+
+## Registry invariants
+
+Every high-impact registry item should eventually expose decision metadata:
+
+- intent;
+- when to use;
+- when not to use;
+- safe variants and what they mean;
+- common misuses;
+- related components;
+- token roles;
+- optional Ghost node or check reference ids when a consuming repo provides them.
+
+## Escape hatch policy
+
+Vessel remains source-owned and shadcn-compatible, so escape hatches are not
+banned categorically. They are governed:
+
+- Prefer variants, slots, tokens, and safe primitives before `className`.
+- Prefer adding a named decision to Vessel when the same override recurs.
+- Keep arbitrary values, inline styles, and raw palette utilities out of normal
+ component source unless there is a documented technical reason.
+- Add checks that can count or reject unsafe paths before relying on review prose.
+
+## Migration order this document protects
+
+1. Reconcile with latest shadcn for upstream hygiene.
+2. Normalize the agnostic token contract.
+3. Re-apply Vessel's own restrained reference stance.
+4. Add Orbit safety: checks, metadata, and safer component APIs.
+5. Mine downstream product forks selectively for reusable AI/product patterns.
+
+Do not invert this into "copy a product fork" or "accept latest shadcn
+wholesale." Both would erase the reason Vessel exists.
diff --git a/ideas/ci-pr-integration-recipes.md b/ideas/ci-pr-integration-recipes.md
deleted file mode 100644
index 4f8bdfcf..00000000
--- a/ideas/ci-pr-integration-recipes.md
+++ /dev/null
@@ -1,87 +0,0 @@
-# Idea: CI / PR integration recipes
-
-_Captured 2026-06-11._
-
-## Problem
-
-People ask "how do I run Ghost on a PR — does it comment something?" Today the
-honest answer is: Ghost ships no PR bot and posts no comments. It deliberately
-stays a calculator. That's the right OSS boundary, but the missing piece is
-**copy-paste examples** so integrators don't have to figure out the glue from
-scratch.
-
-## Design principle (keep this intact)
-
-Ghost the **tool** stays neutral — it commits only to two stable contracts and
-refuses to own the last mile:
-
-| Contract | What it is | So that… |
-| --- | --- | --- |
-| `ghost check --format json` | Documented schema (`ghost.check-report/v1`): result, changed_files, routed_files, findings | …any CI parses pass/fail + findings with no LLM |
-| `ghost review` (text packet) | Self-contained agent prompt on stdout (fingerprint layers + checks + diff) | …any agent consumes it with zero integration code |
-
-Everything else — which CI, which agent, whether it comments — is the
-integrator's choice. This neutrality is exactly what makes it broad (same shape
-as ESLint emitting JSON/SARIF and letting reviewdog/Danger post it).
-
-Ghost the **docs/examples** should still hand people forkable starting points
-for common stacks. Examples lower adoption friction without locking the tool in.
-
-## Universal glue shape
-
-1. CI runs `ghost check` / `ghost review` on the PR diff — **Ghost's job (stable output)**
-2. Integrator decides what to do with the output — **your glue (10–30 lines)**
-3. Post / gate / ignore using your platform's tools (gh, glab, API, Slack…) — **your last mile**
-
-## Two comment strategies (map to the two contracts)
-
-- **Deterministic comment / blocking gate** → consume `check --format json`, fail
- on exit code. No agent. Reproducible. Good default for a required check.
-- **Agent critique comment** → pipe `review` to *your* agent, post what it writes.
- The BYOA taste layer. Advisory only (Ghost intentionally won't let an LLM block
- a build).
-
-Most orgs run both: check gates, review adds a non-blocking design critique.
-
-## Recipes to write
-
-| Recipe | Demonstrates |
-| --- | --- |
-| GitHub Actions — deterministic gate | `ghost check --format json` → fail job on exit code. No agent. |
-| GitHub Actions — agent review comment | `ghost review` → pipe to agent → `gh pr comment`. BYOA layer. |
-| GitLab CI | Same two steps with `glab`. Proves host-agnostic core. |
-| Generic / any CI (plain shell) | Platform-free version for Jenkins/CircleCI/Buildkite. |
-| Pre-commit / local | `ghost check --base HEAD` before pushing. |
-
-## Rules that keep examples OSS-broad
-
-1. **Annotate "Ghost's part" vs "your glue"** in every example, so a reader on a
- different stack sees the seam and can swap the last mile. Forkable, not
- prescriptive.
-2. **Keep the agent step pluggable** — show it as `... | your-agent ...` with one
- or two concrete fills (Claude Code, Codex), never a single hardcoded vendor.
-3. Frame everything as **recipes, not the blessed way**.
-
-## Where this should live (decide before writing)
-
-- `apps/docs` — a "CI & PR Integration" docs page. Most discoverable; matches the
- ESLint Integrations model. (Lean toward this.)
-- `packages/ghost/src/skill-bundle/references/` — as skill recipes, so an installed
- agent can set up the CI for the user. (Also do this.)
-- Top-level `examples/` — liftable files. Optional.
-
-Recommendation: **docs page + skill reference**, with workflow YAML inlined so
-it's literally copy-paste.
-
-## Open questions
-
-- Should `ghost emit` grow a `ci-workflow` kind that scaffolds a starter workflow
- file, the way it already emits `review-command` and `context-bundle`? Would keep
- the "neutral tool" line while still being helpful.
-- Do we document SARIF output for `check` so GitHub code-scanning / reviewdog work
- out of the box?
-
-## Next step
-
-Check where docs/skill references currently live and match existing style, then
-draft the CI Recipes page content for review before it lands.
diff --git a/install/install.sh b/install/install.sh
index 0afd6b83..d6381809 100755
--- a/install/install.sh
+++ b/install/install.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# Ghost — install the unified surface-composition skill bundle.
+# Ghost — install the unified brand-fingerprint skill bundle.
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/block/ghost/main/install/install.sh | sh
@@ -14,9 +14,8 @@
# What gets installed:
# /ghost/
# SKILL.md
-# references/brief.md, capture.md, compare.md, critique.md
-# references/patterns.md, recall.md, remediate.md, review.md
-# references/schema.md, verify.md
+# references/authoring-scenarios.md, brief.md, capture.md
+# references/inventory.md, recall.md, schema.md, self-check.md
#
# Exit codes:
# 0 installed
@@ -194,6 +193,6 @@ printf 'Next:\n'
printf ' cd \n'
printf ' Tell your agent: "Set up the Ghost fingerprint for this repo"\n'
printf '\n'
-printf 'The agent will use .ghost/ as checked-in surface-composition context,\n'
-printf 'generate from intent.yml, inventory.yml, and composition.yml, keep optional\n'
-printf 'deterministic gates in validate.yml, and run ghost lint/verify/check/review.\n'
+printf 'The agent will use .ghost/ as the checked-in brand fingerprint: a flat\n'
+printf 'corpus of prose nodes it gathers and pulls before building, validates\n'
+printf 'with ghost validate, and can review diffs against with ghost review.\n'
diff --git a/install/manifest.json b/install/manifest.json
index 40fa991b..2595f730 100644
--- a/install/manifest.json
+++ b/install/manifest.json
@@ -1,7 +1,7 @@
{
"$schema": "ghost.install-manifest/v1",
"name": "ghost",
- "description": "Ghost recipes — create, brief, review, and preserve the repo-local fingerprint bundle.",
+ "description": "Ghost recipes \u2014 create, author, and gather the repo-local brand fingerprint bundle.",
"version": "0.1.0",
"source": {
"package": "packages/ghost/src/skill-bundle"
@@ -9,16 +9,13 @@
"files": [
"SKILL.md",
"references/authoring-scenarios.md",
+ "references/blocks.md",
"references/brief.md",
"references/capture.md",
- "references/compare.md",
- "references/critique.md",
- "references/patterns.md",
"references/recall.md",
- "references/remediate.md",
- "references/review.md",
"references/schema.md",
- "references/verify.md",
- "references/voice.md"
+ "references/self-check.md",
+ "references/steering-audit.md",
+ "references/wild.md"
]
}
diff --git a/justfile b/justfile
index 89def228..4d4dd7d6 100644
--- a/justfile
+++ b/justfile
@@ -49,13 +49,13 @@ dev:
build-ui:
pnpm -F ghost-docs build
-# Build ghost-ui library (dist-lib + types)
+# Build vessel library (dist-lib + types)
build-lib:
- pnpm -F ghost-ui build:lib
+ pnpm -F vessel build:lib
-# Build ghost-ui shadcn registry
+# Build vessel shadcn registry
build-registry:
- pnpm -F ghost-ui build:registry
+ pnpm -F vessel build:registry
# Build docs site for GitHub Pages (base=/ghost/)
build-pages:
diff --git a/lefthook.yml b/lefthook.yml
index 33aaee43..296df1c1 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -4,6 +4,8 @@ pre-commit:
run: npx biome format --write . && npx biome check --fix . && git add -u
check:
run: just check
+ test:
+ run: just test
pre-push:
parallel: true
diff --git a/package.json b/package.json
index b620e649..72af3b23 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "tsc --build",
- "build:ui": "pnpm --filter ghost-ui build",
+ "build:ui": "pnpm --filter @design-intelligence/vessel build",
"check": "biome check . && pnpm typecheck && pnpm check:package-bin && pnpm check:packed-package && pnpm check:file-sizes && pnpm check:terminology && pnpm check:docs && pnpm check:install-bundle && pnpm check:release-tarball && pnpm check:release-workflows && pnpm check:cli-manifest",
"check:file-sizes": "node scripts/check-file-sizes.mjs",
"check:packed-package": "node scripts/check-packed-package.mjs",
@@ -29,7 +29,7 @@
"lint": "biome lint .",
"changeset": "changeset",
"version-packages": "changeset version",
- "release": "pnpm --filter @anarchitecture/ghost build && changeset publish"
+ "release": "pnpm --filter @design-intelligence/ghost build && changeset publish"
},
"devDependencies": {
"@anthropic-ai/sdk": "^0.90.0",
diff --git a/packages/ghost-fleet/package.json b/packages/ghost-fleet/package.json
deleted file mode 100644
index 7a8b3cc3..00000000
--- a/packages/ghost-fleet/package.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "name": "ghost-fleet",
- "version": "0.0.0",
- "private": true,
- "description": "Read-only elevation view across many (map.md, fingerprint.md) members — pairwise distances, group-by axes, tracks-graph, fleet.md output",
- "license": "Apache-2.0",
- "author": "Block, Inc.",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/block/ghost.git"
- },
- "homepage": "https://github.com/block/ghost#readme",
- "bugs": {
- "url": "https://github.com/block/ghost/issues"
- },
- "keywords": [
- "design-system",
- "fleet",
- "design-language",
- "monorepo",
- "cli"
- ],
- "type": "module",
- "main": "./dist/core/index.js",
- "types": "./dist/core/index.d.ts",
- "bin": {
- "ghost-fleet": "./dist/bin.js"
- },
- "exports": {
- ".": {
- "types": "./dist/core/index.d.ts",
- "import": "./dist/core/index.js"
- },
- "./cli": {
- "types": "./dist/cli.d.ts",
- "import": "./dist/cli.js"
- }
- },
- "files": [
- "dist"
- ],
- "scripts": {
- "build": "rm -rf dist && tsc --build --force && cp -r src/skill-bundle dist/skill-bundle"
- },
- "dependencies": {
- "@anarchitecture/ghost": "workspace:*",
- "cac": "^6.7.14",
- "yaml": "^2.8.3",
- "zod": "^4.3.6"
- }
-}
diff --git a/packages/ghost-fleet/src/bin.ts b/packages/ghost-fleet/src/bin.ts
deleted file mode 100644
index d818272c..00000000
--- a/packages/ghost-fleet/src/bin.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env node
-
-import { existsSync } from "node:fs";
-import { resolve } from "node:path";
-
-// Load .env from the working directory if present.
-for (const envFile of [".env", ".env.local"]) {
- const envPath = resolve(process.cwd(), envFile);
- if (existsSync(envPath)) {
- try {
- process.loadEnvFile(envPath);
- } catch {
- // Node < 20.12 or malformed file — silently skip
- }
- }
-}
-
-import { buildCli } from "./cli.js";
-
-const cli = buildCli();
-cli.parse();
diff --git a/packages/ghost-fleet/src/cli.ts b/packages/ghost-fleet/src/cli.ts
deleted file mode 100644
index d54d6621..00000000
--- a/packages/ghost-fleet/src/cli.ts
+++ /dev/null
@@ -1,229 +0,0 @@
-import { readFileSync } from "node:fs";
-import { mkdir, writeFile } from "node:fs/promises";
-import { dirname, relative, resolve } from "node:path";
-import { fileURLToPath } from "node:url";
-import { loadSkillBundle } from "@anarchitecture/ghost/core";
-import { cac } from "cac";
-import { loadMembers, summarizeMember, writeFleetView } from "./core/index.js";
-
-/**
- * The skill bundle's source files live in `src/skill-bundle/` as real
- * markdown and are copied verbatim into `dist/skill-bundle/` by the
- * package build step. This loader points the shared Ghost skill loader
- * walker at that built directory at runtime.
- */
-const SKILL_BUNDLE_ROOT = fileURLToPath(
- new URL("./skill-bundle", import.meta.url),
-);
-
-const DEFAULT_SKILL_OUT = ".claude/skills/ghost-fleet";
-
-/**
- * Build the cac CLI for `ghost-fleet`.
- *
- * Three deterministic verbs:
- * - `members ` — list registered members + freshness signal
- * - `view ` — emit fleet.md + fleet.json into `/reports/`
- * - `emit skill` — install the fleet agentskills.io bundle into a host agent
- *
- * Temporal aggregation, refresh, and interactive browsing are scoped out of
- * this milestone; see `docs/ghost-fleet.md`.
- */
-export function buildCli(): ReturnType {
- const cli = cac("ghost-fleet");
-
- // --- members ---
- cli
- .command(
- "members [dir]",
- "List registered fleet members and their freshness — one row per (map.md, fingerprint.md) subdirectory.",
- )
- .option("--json", "Output JSON (one object per member) instead of a table")
- .action(async (dir: string | undefined, opts: { json?: boolean }) => {
- try {
- const target = resolve(process.cwd(), dir ?? ".");
- const members = await loadMembers(target);
- const summaries = members.map(summarizeMember);
-
- if (opts.json) {
- process.stdout.write(`${JSON.stringify(summaries, null, 2)}\n`);
- process.exit(0);
- }
-
- if (summaries.length === 0) {
- process.stdout.write(`No members found under ${target}\n`);
- process.exit(0);
- }
-
- process.stdout.write(formatMembersTable(summaries));
- process.exit(0);
- } catch (err) {
- process.stderr.write(
- `Error: ${err instanceof Error ? err.message : String(err)}\n`,
- );
- process.exit(2);
- }
- });
-
- // --- view ---
- cli
- .command(
- "view [dir]",
- "Compute the fleet's pairwise distances, group-by tables, and tracks-graph; emit fleet.md + fleet.json into /reports/.",
- )
- .option(
- "--id ",
- "Override the fleet id (default: directory basename slug)",
- )
- .option("--out ", "Reports directory (default: /reports)")
- .action(
- async (dir: string | undefined, opts: { id?: string; out?: string }) => {
- try {
- const target = resolve(process.cwd(), dir ?? ".");
- const result = await writeFleetView(target, {
- id: opts.id,
- outDir: opts.out,
- });
- const cwd = process.cwd();
- process.stdout.write(
- `Wrote ${result.files.length} file${
- result.files.length === 1 ? "" : "s"
- } to ${displayPath(cwd, result.outDir)}:\n`,
- );
- for (const f of result.files) {
- process.stdout.write(` ${f}\n`);
- }
- const memberCount = result.view.members.length;
- const distanceCount = result.view.distances.length;
- const nodeCount = result.view.nodes.length;
- const nodeDistanceCount = result.view.node_distances.length;
- process.stdout.write(
- `\n${memberCount} member${memberCount === 1 ? "" : "s"}, ${distanceCount} pairwise distance${
- distanceCount === 1 ? "" : "s"
- }, ${nodeCount} fingerprint node${nodeCount === 1 ? "" : "s"}, ${nodeDistanceCount} node distance${
- nodeDistanceCount === 1 ? "" : "s"
- }, ${result.view.tracks.length} track edge${
- result.view.tracks.length === 1 ? "" : "s"
- }\n`,
- );
- process.exit(0);
- } catch (err) {
- process.stderr.write(
- `Error: ${err instanceof Error ? err.message : String(err)}\n`,
- );
- process.exit(2);
- }
- },
- );
-
- // --- emit skill ---
- cli
- .command(
- "emit ",
- "Emit the ghost-fleet agentskills.io bundle (kind: skill).",
- )
- .option(
- "-o, --out ",
- `Output directory (default: ${DEFAULT_SKILL_OUT})`,
- )
- .action(async (kind: string, opts: { out?: string }) => {
- try {
- if (kind !== "skill") {
- process.stderr.write(
- `Error: unknown emit kind '${kind}'. Supported: skill.\n`,
- );
- process.exit(2);
- return;
- }
-
- const outDir = resolve(process.cwd(), opts.out ?? DEFAULT_SKILL_OUT);
- const bundle = loadSkillBundle(SKILL_BUNDLE_ROOT);
- const written: string[] = [];
- for (const file of bundle) {
- const outPath = resolve(outDir, file.path);
- await mkdir(dirname(outPath), { recursive: true });
- await writeFile(outPath, file.content, "utf-8");
- written.push(file.path);
- }
- process.stdout.write(
- `Wrote ${written.length} file${
- written.length === 1 ? "" : "s"
- } to ${outDir}:\n`,
- );
- for (const f of written) process.stdout.write(` ${f}\n`);
- process.exit(0);
- } catch (err) {
- process.stderr.write(
- `Error: ${err instanceof Error ? err.message : String(err)}\n`,
- );
- process.exit(2);
- }
- });
-
- cli.help();
- cli.version(readPackageVersion());
-
- return cli;
-}
-
-/**
- * Format the members table for human stdout.
- *
- * Columns: id, platform, build_system, registry, fingerprint mtime, status.
- */
-function formatMembersTable(
- summaries: ReturnType[],
-): string {
- const headers = [
- "ID",
- "PLATFORM",
- "BUILD",
- "REGISTRY",
- "FINGERPRINT",
- "STATUS",
- ];
- const rows = summaries.map((s) => [
- s.id,
- formatCellValue(s.platform),
- formatCellValue(s.build_system),
- s.registry ?? "-",
- s.fingerprint_mtime ? s.fingerprint_mtime.slice(0, 10) : "-",
- s.ok ? "ok" : `${s.mapStatus}/${s.fingerprintStatus}`,
- ]);
- const widths = headers.map((h, i) =>
- Math.max(h.length, ...rows.map((r) => r[i]?.length ?? 0)),
- );
- const fmt = (cells: string[]) =>
- cells.map((c, i) => c.padEnd(widths[i] ?? 0)).join(" ");
- const out: string[] = [fmt(headers), fmt(widths.map((w) => "-".repeat(w)))];
- for (const row of rows) out.push(fmt(row));
- return `${out.join("\n")}\n`;
-}
-
-/**
- * Format a single-or-array `MemberSummary` cell for the human table.
- * Arrays render as comma-separated values so multi-platform repos show
- * all members in one row.
- */
-function formatCellValue(value: string | string[] | null | undefined): string {
- if (value === null || value === undefined) return "-";
- if (Array.isArray(value)) {
- if (value.length === 0) return "-";
- return value.join(",");
- }
- return value;
-}
-
-function displayPath(cwd: string, target: string): string {
- const rel = relative(cwd, target);
- if (rel.length > 0 && !rel.startsWith("..")) return rel;
- return target;
-}
-
-function readPackageVersion(): string {
- const here = dirname(fileURLToPath(import.meta.url));
- const pkg = JSON.parse(
- readFileSync(resolve(here, "../package.json"), "utf8"),
- );
- return pkg.version as string;
-}
diff --git a/packages/ghost-fleet/src/core/compute.ts b/packages/ghost-fleet/src/core/compute.ts
deleted file mode 100644
index 47ea0389..00000000
--- a/packages/ghost-fleet/src/core/compute.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import { compareFingerprints } from "@anarchitecture/ghost/compare";
-import type {
- FleetGroupingsComputed,
- FleetMember,
- FleetPairwise,
- FleetTrack,
-} from "./types.js";
-
-/**
- * Compute the pairwise distance array between every member that has a
- * valid fingerprint. Members without a loadable fingerprint are dropped
- * from the matrix — they still appear in the members table.
- *
- * Order: ascending by `(a, b)` id pair, so the JSON output is reproducible.
- */
-export function computePairwiseDistances(
- members: FleetMember[],
-): FleetPairwise[] {
- const eligible = members.filter(
- (m) => m.fingerprint && m.fingerprintStatus === "ok",
- );
-
- const out: FleetPairwise[] = [];
- for (let i = 0; i < eligible.length; i++) {
- for (let j = i + 1; j < eligible.length; j++) {
- const a = eligible[i];
- const b = eligible[j];
- if (!a.fingerprint || !b.fingerprint) continue;
- const cmp = compareFingerprints(a.fingerprint, b.fingerprint);
- out.push({ a: a.id, b: b.id, distance: cmp.distance });
- }
- }
-
- return out.sort((x, y) => {
- if (x.a !== y.a) return x.a.localeCompare(y.a);
- return x.b.localeCompare(y.b);
- });
-}
-
-/**
- * Compute distances across every loaded parent/scope fingerprint node.
- * Parent-only `computePairwiseDistances` stays unchanged for compatibility.
- */
-export function computeNodeDistances(members: FleetMember[]): FleetPairwise[] {
- const nodes = members.flatMap((member) => member.fingerprintNodes);
-
- const out: FleetPairwise[] = [];
- for (let i = 0; i < nodes.length; i++) {
- for (let j = i + 1; j < nodes.length; j++) {
- const a = nodes[i];
- const b = nodes[j];
- const cmp = compareFingerprints(a.fingerprint, b.fingerprint);
- out.push({ a: a.id, b: b.id, distance: cmp.distance });
- }
- }
-
- return out.sort((x, y) => {
- if (x.a !== y.a) return x.a.localeCompare(y.a);
- return x.b.localeCompare(y.b);
- });
-}
-
-/**
- * Compute the five group-by axes from each member's map.md frontmatter.
- *
- * Axes per `docs/ghost-fleet.md`:
- * - by_platform map.platform
- * - by_build_system map.build_system
- * - by_registry map.registry?.path ?? "none"
- * - by_rendering map.composition.rendering
- * - by_styling map.composition.styling[0]
- *
- * Members without a parsed map.md are skipped — they show up in the
- * members table but not in the groupings.
- */
-export function computeGroupings(
- members: FleetMember[],
-): FleetGroupingsComputed {
- const groupings: FleetGroupingsComputed = {
- by_platform: {},
- by_build_system: {},
- by_registry: {},
- by_rendering: {},
- by_styling: {},
- };
-
- for (const member of members) {
- const map = member.map;
- if (!map) continue;
-
- // platform / build_system may be a string OR an array — the fleet
- // groupings cross-tabulate per value, so an array contributes the
- // member to each survey it names.
- for (const value of toArray(map.platform)) {
- push(groupings.by_platform, value, member.id);
- }
- for (const value of toArray(map.build_system)) {
- push(groupings.by_build_system, value, member.id);
- }
- push(groupings.by_registry, map.registry ? "shadcn" : "none", member.id);
-
- const rendering = map.composition.rendering;
- push(groupings.by_rendering, rendering, member.id);
-
- const primaryStyling = map.composition.styling[0];
- if (primaryStyling) {
- push(groupings.by_styling, primaryStyling, member.id);
- }
- }
-
- // Sort each axis survey so output is deterministic.
- for (const axis of Object.values(groupings) as Record[]) {
- for (const key of Object.keys(axis)) {
- axis[key]?.sort((a, b) => a.localeCompare(b));
- }
- }
-
- return groupings;
-}
-
-function push(
- survey: Record,
- key: string | undefined,
- id: string,
-): void {
- if (!key) return;
- if (!survey[key]) survey[key] = [];
- survey[key].push(id);
-}
-
-/** Normalize a string-or-array map field to an array of strings. */
-function toArray(value: T | T[] | undefined): T[] {
- if (value === undefined) return [];
- return Array.isArray(value) ? value : [value];
-}
-
-/**
- * Build the tracks-graph from each member's recorded `tracks` target.
- *
- * Edges read directly from `.ghost-sync.json` — fleet does not author
- * relationships. The right-hand side is whatever the member declared; the
- * skill recipe interprets whether it resolves to another member id or an
- * external reference.
- */
-export function computeTracks(members: FleetMember[]): FleetTrack[] {
- const out: FleetTrack[] = [];
- for (const member of members) {
- if (!member.tracks) continue;
- out.push({ from: member.id, to: member.tracks });
- }
- return out.sort((x, y) => {
- if (x.from !== y.from) return x.from.localeCompare(y.from);
- return x.to.localeCompare(y.to);
- });
-}
diff --git a/packages/ghost-fleet/src/core/index.ts b/packages/ghost-fleet/src/core/index.ts
deleted file mode 100644
index 5ebdd35d..00000000
--- a/packages/ghost-fleet/src/core/index.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Public library surface for the `ghost-fleet` package.
- *
- * Mirrors the other ghost-* packages: a single barrel that consumers
- * import from `ghost-fleet` (no deep imports required).
- */
-
-export {
- computeGroupings,
- computeNodeDistances,
- computePairwiseDistances,
- computeTracks,
-} from "./compute.js";
-export { loadMembers, summarizeMember } from "./members.js";
-export type {
- FleetDistance,
- FleetFingerprintNodeEntry,
- FleetFrontmatter,
- FleetGroupings,
- FleetMemberEntry,
- FleetTrackEdge,
- RequiredBodySection,
-} from "./schema.js";
-export {
- FLEET_FILENAME,
- FLEET_JSON_FILENAME,
- FLEET_MEMBERS_DIRNAME,
- FLEET_REPORTS_DIRNAME,
- FleetFrontmatterSchema,
- REQUIRED_BODY_SECTIONS,
-} from "./schema.js";
-export type {
- FleetFingerprintNode,
- FleetGroupingsComputed,
- FleetMember,
- FleetPairwise,
- FleetTrack,
- FleetView,
- MemberFileStatus,
- MemberSummary,
-} from "./types.js";
-export type {
- BuildViewOptions,
- BuildViewResult,
- WriteViewOptions,
- WriteViewResult,
-} from "./view.js";
-export {
- buildFleetView,
- renderFleetJson,
- renderFleetMarkdown,
- writeFleetView,
-} from "./view.js";
diff --git a/packages/ghost-fleet/src/core/members.ts b/packages/ghost-fleet/src/core/members.ts
deleted file mode 100644
index f836932f..00000000
--- a/packages/ghost-fleet/src/core/members.ts
+++ /dev/null
@@ -1,293 +0,0 @@
-import { existsSync, readdirSync, statSync } from "node:fs";
-import { readFile, stat } from "node:fs/promises";
-import { join, resolve } from "node:path";
-import {
- getEffectiveMapScopes,
- MAP_FILENAME,
- type MapFrontmatter,
- MapFrontmatterSchema,
- type MapScope,
-} from "@anarchitecture/ghost/core";
-import {
- FINGERPRINT_FILENAME,
- loadFingerprint,
-} from "@anarchitecture/ghost/fingerprint";
-import { parse as parseYaml } from "yaml";
-import { FLEET_MEMBERS_DIRNAME } from "./schema.js";
-import type { FleetMember, MemberSummary } from "./types.js";
-
-const FINGERPRINTS_DIRNAME = "fingerprints";
-
-/**
- * Walk the canonical fleet layout and produce one FleetMember per
- * subdirectory under `/members/`.
- *
- * The fleet root is the directory you'd pass to `ghost fleet view`. If a
- * `members/` subdir exists, members live there; otherwise we fall back to
- * treating the passed-in directory itself as the members root, so tooling
- * can also point at a flat `members/` directory directly.
- *
- * This never refreshes anything. Missing or malformed files are surfaced via
- * per-member status; nothing is fetched.
- */
-export async function loadMembers(dir: string): Promise {
- const root = resolve(dir);
- const membersRoot = pickMembersRoot(root);
-
- if (!existsSync(membersRoot)) return [];
-
- const entries = readdirSync(membersRoot, { withFileTypes: true })
- .filter((e) => e.isDirectory())
- .filter((e) => !e.name.startsWith("."));
-
- const members = await Promise.all(
- entries.map((entry) => loadMember(join(membersRoot, entry.name))),
- );
-
- // Stable order — id ascending — so reports diff cleanly.
- return members.sort((a, b) => a.id.localeCompare(b.id));
-}
-
-/**
- * Resolve the members directory.
- *
- * Convention is `/members//{map.md,fingerprint.md}`. We also
- * accept being pointed directly at a `members/` directory.
- */
-function pickMembersRoot(root: string): string {
- const candidate = join(root, FLEET_MEMBERS_DIRNAME);
- if (existsSync(candidate)) {
- try {
- return statSync(candidate).isDirectory() ? candidate : root;
- } catch {
- return root;
- }
- }
- return root;
-}
-
-/**
- * Load a single member directory.
- *
- * Reads map.md, fingerprint.md, and optional .ghost-sync.json. Each is
- * surfaced through a status field so missing/broken inputs are visible
- * without crashing the rest of the load.
- */
-async function loadMember(memberPath: string): Promise {
- const dirName = memberPath.split("/").pop() ?? "";
-
- const mapPath = join(memberPath, MAP_FILENAME);
- const fingerprintPath = join(memberPath, FINGERPRINT_FILENAME);
-
- // Default identity is the directory basename; map.md `id` overrides.
- let id = dirName;
-
- // --- map.md ---
- let map: MapFrontmatter | undefined;
- let mapStatus: FleetMember["mapStatus"] = "missing";
- let mapError: string | undefined;
- if (existsSync(mapPath)) {
- try {
- const raw = await readFile(mapPath, "utf-8");
- map = parseMapFrontmatter(raw);
- if (map?.id) id = map.id;
- mapStatus = "ok";
- } catch (err) {
- mapStatus = "error";
- mapError = err instanceof Error ? err.message : String(err);
- }
- }
-
- // --- fingerprint.md ---
- let fingerprintStatus: FleetMember["fingerprintStatus"] = "missing";
- let fingerprintError: string | undefined;
- let fingerprint: FleetMember["fingerprint"];
- let fingerprintMtime: string | undefined;
- const fingerprintNodes: FleetMember["fingerprintNodes"] = [];
- if (existsSync(fingerprintPath)) {
- try {
- const parsed = await loadFingerprint(fingerprintPath);
- fingerprint = parsed.fingerprint;
- const mtime = (await stat(fingerprintPath)).mtime;
- fingerprintMtime = mtime.toISOString();
- fingerprintStatus = "ok";
- fingerprintNodes.push({
- id,
- memberId: id,
- kind: "member",
- fingerprint,
- fingerprintPath,
- fingerprintMtime,
- });
- } catch (err) {
- fingerprintStatus = "error";
- fingerprintError = err instanceof Error ? err.message : String(err);
- }
- }
-
- fingerprintNodes.push(
- ...(await loadScopedFingerprintNodes(memberPath, id, map)),
- );
-
- // --- .ghost-sync.json (optional) ---
- const tracks = await readTracksTarget(memberPath);
-
- return {
- id,
- path: memberPath,
- map,
- mapStatus,
- mapError,
- fingerprint,
- fingerprintStatus,
- fingerprintError,
- fingerprintMtime,
- tracks,
- fingerprintNodes,
- };
-}
-
-async function loadScopedFingerprintNodes(
- memberPath: string,
- memberId: string,
- map: MapFrontmatter | undefined,
-): Promise {
- const scopesDir = join(memberPath, FINGERPRINTS_DIRNAME);
- if (!existsSync(scopesDir)) return [];
-
- const scopeById = new Map();
- if (map) {
- for (const scope of getEffectiveMapScopes(map)) {
- scopeById.set(scope.id, scope);
- }
- }
-
- const entries = readdirSync(scopesDir, { withFileTypes: true })
- .filter((entry) => entry.isFile())
- .filter((entry) => entry.name.endsWith(".md"))
- .sort((a, b) => a.name.localeCompare(b.name));
-
- const nodes: FleetMember["fingerprintNodes"] = [];
- for (const entry of entries) {
- const scopeId = entry.name.slice(0, -".md".length);
- const fingerprintPath = join(scopesDir, entry.name);
- try {
- const parsed = await loadFingerprint(fingerprintPath);
- const fingerprintMtime = (
- await stat(fingerprintPath)
- ).mtime.toISOString();
- const scope = scopeById.get(scopeId);
- nodes.push({
- id: `${memberId}/${scopeId}`,
- memberId,
- kind: "scope",
- fingerprint: parsed.fingerprint,
- fingerprintPath,
- fingerprintMtime,
- scopeId,
- parentId: memberId,
- ...(scope ? { scope } : {}),
- });
- } catch {
- // Parent member status remains focused on canonical map/fingerprint.
- // Malformed scoped overlays simply don't enter the fleet distance graph.
- }
- }
-
- return nodes;
-}
-
-/**
- * Best-effort parse of a map.md frontmatter block.
- *
- * We don't run the full map linter here — fleet's job is to load,
- * not validate. The schema check still rejects clearly-broken frontmatter
- * so callers get a typed `MapFrontmatter` or nothing.
- */
-function parseMapFrontmatter(raw: string): MapFrontmatter | undefined {
- const split = splitFrontmatter(raw);
- if (!split) return undefined;
- const yamlObj = parseYaml(split.frontmatter);
- if (yamlObj === null || typeof yamlObj !== "object") return undefined;
- const result = MapFrontmatterSchema.safeParse(yamlObj);
- if (!result.success) {
- throw new Error(
- `map.md frontmatter failed validation: ${result.error.issues
- .map((i) => `${i.path.join(".") || ""}: ${i.message}`)
- .join("; ")}`,
- );
- }
- return result.data;
-}
-
-function splitFrontmatter(
- raw: string,
-): { frontmatter: string; body: string } | null {
- const stripped = raw.replace(/^/, "");
- if (!stripped.startsWith("---")) return null;
- const lines = stripped.split(/\r?\n/);
- if (lines[0]?.trim() !== "---") return null;
- let endIndex = -1;
- for (let i = 1; i < lines.length; i++) {
- if (lines[i]?.trim() === "---") {
- endIndex = i;
- break;
- }
- }
- if (endIndex === -1) return null;
- return {
- frontmatter: lines.slice(1, endIndex).join("\n"),
- body: lines.slice(endIndex + 1).join("\n"),
- };
-}
-
-/**
- * Read `.ghost-sync.json` and surface its `tracks` field.
- *
- * Fleet doesn't interpret the value — `tracks` may be a target string
- * (`github:org/repo`), a registered fleet member id, or a local path. The
- * skill recipe decides whether the edge points at another member.
- */
-async function readTracksTarget(
- memberPath: string,
-): Promise {
- const syncPath = join(memberPath, ".ghost-sync.json");
- if (!existsSync(syncPath)) return undefined;
- try {
- const data = JSON.parse(await readFile(syncPath, "utf-8"));
- const tracks = data?.tracks;
- if (typeof tracks === "string") return tracks;
- // The shared SyncManifest also allows `tracks` to be a Target object.
- if (tracks && typeof tracks === "object") {
- if (typeof tracks.id === "string") return tracks.id;
- if (typeof tracks.target === "string") return tracks.target;
- }
- return undefined;
- } catch {
- return undefined;
- }
-}
-
-/**
- * Compact summary row for `ghost fleet members`.
- *
- * Surfaces the freshness signal (fingerprint mtime) and the axes that the
- * group-by tables use, so the CLI can render either a human table or a
- * machine-readable JSON line per member.
- */
-export function summarizeMember(member: FleetMember): MemberSummary {
- const platform = member.map?.platform ?? null;
- const build_system = member.map?.build_system ?? null;
- const registry = member.map?.registry ? member.map.registry.path : null;
-
- return {
- id: member.id,
- platform,
- build_system,
- registry,
- fingerprint_mtime: member.fingerprintMtime ?? null,
- ok: member.mapStatus === "ok" && member.fingerprintStatus === "ok",
- mapStatus: member.mapStatus,
- fingerprintStatus: member.fingerprintStatus,
- };
-}
diff --git a/packages/ghost-fleet/src/core/schema.ts b/packages/ghost-fleet/src/core/schema.ts
deleted file mode 100644
index 6e1c14fa..00000000
--- a/packages/ghost-fleet/src/core/schema.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { z } from "zod";
-
-/**
- * Zod schema for `ghost.fleet/v1` frontmatter.
- *
- * The body sections (World shape / Cohorts / Tracks) are checked separately
- * — this schema only covers the YAML machine layer.
- *
- * Per `docs/ghost-fleet.md`, clusters are deliberately *not* in the
- * frontmatter. They're a body-narrative projection the skill recipe writes over
- * the pairwise distances + groupings the CLI emits.
- */
-
-const ISO_DATE_OR_DATETIME = z.iso.datetime({ offset: true }).or(
- z.string().regex(/^\d{4}-\d{2}-\d{2}$/, {
- message: "must be ISO date (YYYY-MM-DD) or full datetime",
- }),
-);
-
-const StringOrArraySchema = z.union([
- z.string().min(1),
- z.array(z.string().min(1)).min(1),
-]);
-
-export const FleetMemberEntrySchema = z.object({
- id: z
- .string()
- .min(1)
- .regex(/^[a-z0-9][a-z0-9._-]*$/, {
- message:
- "id must be a slug (lowercase alphanumeric plus . _ -, leading alphanumeric)",
- }),
- platform: StringOrArraySchema,
- build_system: StringOrArraySchema.optional(),
- registry: z.string().min(1).nullable().optional(),
- fingerprint_at: ISO_DATE_OR_DATETIME.optional(),
-});
-
-export const FleetFingerprintNodeSchema = z.object({
- id: z.string().min(1),
- member_id: z.string().min(1),
- kind: z.enum(["member", "scope"]),
- scope_id: z.string().min(1).optional(),
- parent_id: z.string().min(1).optional(),
- platform: StringOrArraySchema,
- build_system: StringOrArraySchema.optional(),
- registry: z.string().min(1).nullable().optional(),
- fingerprint_at: ISO_DATE_OR_DATETIME.optional(),
-});
-
-export const FleetDistanceSchema = z.object({
- a: z.string().min(1),
- b: z.string().min(1),
- distance: z.number().min(0),
-});
-
-export const FleetTrackEdgeSchema = z.object({
- from: z.string().min(1),
- to: z.string().min(1),
-});
-
-export const FleetGroupingsSchema = z.record(
- z.string(),
- z.record(z.string(), z.array(z.string().min(1))),
-);
-
-export const FleetFrontmatterSchema = z.object({
- schema: z.literal("ghost.fleet/v1"),
- id: z
- .string()
- .min(1)
- .regex(/^[a-z0-9][a-z0-9._-]*$/, {
- message:
- "id must be a slug (lowercase alphanumeric plus . _ -, leading alphanumeric)",
- }),
- generated_at: ISO_DATE_OR_DATETIME,
- members: z.array(FleetMemberEntrySchema).min(1),
- distances: z.array(FleetDistanceSchema),
- nodes: z.array(FleetFingerprintNodeSchema),
- node_distances: z.array(FleetDistanceSchema),
- tracks: z.array(FleetTrackEdgeSchema),
- groupings: FleetGroupingsSchema,
-});
-
-export type FleetFrontmatter = z.infer;
-export type FleetMemberEntry = z.infer;
-export type FleetFingerprintNodeEntry = z.infer<
- typeof FleetFingerprintNodeSchema
->;
-export type FleetDistance = z.infer;
-export type FleetTrackEdge = z.infer;
-export type FleetGroupings = z.infer;
-
-/** Required body sections in canonical order. */
-export const REQUIRED_BODY_SECTIONS = [
- "World shape",
- "Cohorts",
- "Tracks",
-] as const;
-export type RequiredBodySection = (typeof REQUIRED_BODY_SECTIONS)[number];
-
-/** Canonical filename for the emitted fleet artifact. */
-export const FLEET_FILENAME = "fleet.md";
-/** Canonical filename for the JSON sidecar. */
-export const FLEET_JSON_FILENAME = "fleet.json";
-/** Reports subdirectory under the fleet root where artifacts are written. */
-export const FLEET_REPORTS_DIRNAME = "reports";
-/** Members subdirectory under the fleet root containing per-member files. */
-export const FLEET_MEMBERS_DIRNAME = "members";
diff --git a/packages/ghost-fleet/src/core/types.ts b/packages/ghost-fleet/src/core/types.ts
deleted file mode 100644
index bc7a3dc7..00000000
--- a/packages/ghost-fleet/src/core/types.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * Shared types for the ghost-fleet package.
- *
- * Fleet is read-only over a directory of (map.md, fingerprint.md) members.
- * These types describe how members are loaded, what facts the CLI computes
- * over them, and what the deterministic artifacts look like on disk.
- */
-
-import type {
- Fingerprint,
- MapFrontmatter,
- MapScope,
-} from "@anarchitecture/ghost/core";
-import type { FleetDistance, FleetTrackEdge } from "./schema.js";
-
-/**
- * Lint status for a single member's fingerprint.md and map.md.
- *
- * Three states keep the surface small:
- * • "ok" — the file exists and parses; we don't run the full linter
- * here (that's `ghost lint`).
- * • "missing" — the file is absent from the member directory.
- * • "error" — the file is present but fails to load/parse.
- */
-export type MemberFileStatus = "ok" | "missing" | "error";
-
-/**
- * One member of the fleet — a subdirectory under `fleet/members/`.
- *
- * The CLI populates this from on-disk reads; nothing is fetched and
- * nothing is recomputed.
- */
-export interface FleetMember {
- /** Member identity. Defaults to the directory basename if no map.md. */
- id: string;
- /** Absolute path to the member's directory. */
- path: string;
- /** Parsed map.md frontmatter when present. */
- map?: MapFrontmatter;
- /** Lint/load status of the member's map.md. */
- mapStatus: MemberFileStatus;
- /** Reason when mapStatus is "error". */
- mapError?: string;
- /** Loaded fingerprint with embedding backfilled. */
- fingerprint?: Fingerprint;
- /** Lint/load status of the member's fingerprint.md. */
- fingerprintStatus: MemberFileStatus;
- /** Reason when fingerprintStatus is "error". */
- fingerprintError?: string;
- /** ISO date string of the fingerprint.md mtime when present. */
- fingerprintMtime?: string;
- /** Parsed `.ghost-sync.json` `tracks.id`/string when present. */
- tracks?: string;
- /** Parent + scoped fingerprint nodes that loaded successfully. */
- fingerprintNodes: FleetFingerprintNode[];
-}
-
-export interface FleetFingerprintNode {
- id: string;
- memberId: string;
- kind: "member" | "scope";
- fingerprint: Fingerprint;
- fingerprintPath: string;
- fingerprintMtime?: string;
- scopeId?: string;
- scope?: MapScope;
- parentId?: string;
-}
-
-/**
- * Compact freshness summary for `ghost fleet members`.
- *
- * Mirrors the per-row JSON the CLI emits with `--json`.
- */
-export interface MemberSummary {
- id: string;
- /**
- * Single value or array — mirrors the on-disk `map.platform` shape
- * (Phase 4b made arrays a first-class form for multi-platform repos).
- * `null` when the member has no map.
- */
- platform: string | string[] | null;
- /**
- * Single value or array — mirrors the on-disk `map.build_system` shape
- * (Phase 4b extension for repos that run multiple build systems).
- */
- build_system: string | string[] | null;
- registry: string | null;
- fingerprint_mtime: string | null;
- /** Both files present and parsed. */
- ok: boolean;
- /** Per-file status, surfaced so consumers can render specific cells. */
- mapStatus: MemberFileStatus;
- fingerprintStatus: MemberFileStatus;
-}
-
-/**
- * Group-by axes the CLI computes from each member's map.md frontmatter.
- *
- * Per the plan, fleet exposes five axes:
- * - platform
- * - build_system
- * - registry ("none" when null)
- * - composition.rendering
- * - composition.styling[0]
- */
-export interface FleetGroupingsComputed {
- by_platform: Record;
- by_build_system: Record;
- by_registry: Record;
- by_rendering: Record;
- by_styling: Record;
-}
-
-/**
- * Tracks edge derived from a member's `.ghost-sync.json` `tracks` field.
- *
- * `from` is the member id; `to` is whatever string was recorded under
- * `tracks` (a target string like `github:org/repo`, an id, or a path).
- * Fleet does not interpret the right-hand side — it surfaces the edge as
- * the member declared it. The skill recipe is responsible for deciding
- * whether `to` resolves to another member id or an external reference.
- */
-export type FleetTrack = FleetTrackEdge;
-
-/** Pairwise distances between members. Same shape as the schema. */
-export type FleetPairwise = FleetDistance;
-
-/**
- * Composite view the CLI emits as `fleet.json` and as the frontmatter of
- * `fleet.md`. Body narrative (clusters, prose) is the skill's job.
- */
-export interface FleetView {
- schema: "ghost.fleet/v1";
- id: string;
- generated_at: string;
- members: Array<{
- id: string;
- platform: string | string[];
- build_system?: string | string[];
- registry: string | null;
- fingerprint_at?: string;
- }>;
- distances: FleetPairwise[];
- nodes: Array<{
- id: string;
- member_id: string;
- kind: "member" | "scope";
- scope_id?: string;
- parent_id?: string;
- platform: string | string[];
- build_system?: string | string[];
- registry: string | null;
- fingerprint_at?: string;
- }>;
- node_distances: FleetPairwise[];
- tracks: FleetTrack[];
- groupings: FleetGroupingsComputed;
-}
diff --git a/packages/ghost-fleet/src/core/view.ts b/packages/ghost-fleet/src/core/view.ts
deleted file mode 100644
index 9c087c54..00000000
--- a/packages/ghost-fleet/src/core/view.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-import { mkdir, writeFile } from "node:fs/promises";
-import { basename, join, resolve } from "node:path";
-import { stringify as stringifyYaml } from "yaml";
-import {
- computeGroupings,
- computeNodeDistances,
- computePairwiseDistances,
- computeTracks,
-} from "./compute.js";
-import { loadMembers } from "./members.js";
-import {
- FLEET_FILENAME,
- FLEET_JSON_FILENAME,
- FLEET_REPORTS_DIRNAME,
- REQUIRED_BODY_SECTIONS,
-} from "./schema.js";
-import type { FleetMember, FleetView } from "./types.js";
-
-export interface BuildViewOptions {
- /** Optional override for the fleet id. Defaults to the directory basename. */
- id?: string;
- /**
- * Override the timestamp used for `generated_at`. Defaults to a fresh
- * `new Date()` — exposed so tests get reproducible output without
- * monkey-patching globals.
- */
- now?: Date;
- /** Optional preloaded members (saves a directory walk in tests). */
- members?: FleetMember[];
-}
-
-export interface BuildViewResult {
- view: FleetView;
- members: FleetMember[];
-}
-
-/**
- * Compose the deterministic FleetView for a given fleet directory.
- *
- * Pure: no filesystem writes. The CLI's `view` verb wraps this with
- * file output. Body narrative is the skill's job — this function builds
- * frontmatter-shaped data only.
- */
-export async function buildFleetView(
- dir: string,
- options: BuildViewOptions = {},
-): Promise {
- const root = resolve(dir);
- const members = options.members ?? (await loadMembers(root));
-
- const distances = computePairwiseDistances(members);
- const node_distances = computeNodeDistances(members);
- const groupings = computeGroupings(members);
- const tracks = computeTracks(members);
-
- const generated_at = (options.now ?? new Date()).toISOString().slice(0, 10);
- const id = options.id ?? defaultFleetId(root);
-
- const memberRows = members
- .filter((m) => m.map)
- .map((member) => {
- const map = member.map;
- if (!map) throw new Error("unreachable: member.map missing after filter");
- const row: FleetView["members"][number] = {
- id: member.id,
- platform: map.platform,
- build_system: map.build_system,
- registry: map.registry ? map.registry.path : null,
- };
- if (member.fingerprintMtime) {
- row.fingerprint_at = member.fingerprintMtime.slice(0, 10);
- }
- return row;
- });
-
- const nodes = members.flatMap((member) => {
- const map = member.map;
- if (!map) return [];
- return member.fingerprintNodes.map((node) => {
- const row: FleetView["nodes"][number] = {
- id: node.id,
- member_id: node.memberId,
- kind: node.kind,
- platform: map.platform,
- build_system: map.build_system,
- registry: map.registry ? map.registry.path : null,
- };
- if (node.scopeId) row.scope_id = node.scopeId;
- if (node.parentId) row.parent_id = node.parentId;
- if (node.fingerprintMtime) {
- row.fingerprint_at = node.fingerprintMtime.slice(0, 10);
- }
- return row;
- });
- });
-
- const view: FleetView = {
- schema: "ghost.fleet/v1",
- id,
- generated_at,
- members: memberRows,
- distances,
- nodes,
- node_distances,
- tracks,
- groupings,
- };
-
- return { view, members };
-}
-
-function defaultFleetId(root: string): string {
- const base = basename(root);
- const slug = base
- .toLowerCase()
- .replace(/[^a-z0-9._-]+/g, "-")
- .replace(/^[^a-z0-9]+/, "");
- return slug.length > 0 ? slug : "fleet";
-}
-
-/**
- * Render the view as a `fleet.md` source string — frontmatter plus the
- * body skeleton the skill recipe fills in.
- *
- * The CLI does not author the narrative. The body's three required sections
- * (`## World shape`, `## Cohorts`, `## Tracks`) appear with empty placeholder
- * paragraphs so the skill writer has clear targets and `lint` could later
- * check section presence.
- */
-export function renderFleetMarkdown(view: FleetView): string {
- const yaml = stringifyYaml(view);
- const body = REQUIRED_BODY_SECTIONS.map((section) => {
- return [
- `## ${section}`,
- "",
- ``,
- "",
- ].join("\n");
- }).join("\n");
- return `---\n${yaml}---\n\n${body}`;
-}
-
-/**
- * Render the view as a stable JSON sidecar.
- *
- * Same shape as the frontmatter, sorted-key serialization so diffs are
- * reproducible across runs.
- */
-export function renderFleetJson(view: FleetView): string {
- return `${JSON.stringify(view, null, 2)}\n`;
-}
-
-export interface WriteViewOptions extends BuildViewOptions {
- /** Reports directory; defaults to `/reports`. */
- outDir?: string;
-}
-
-export interface WriteViewResult {
- view: FleetView;
- members: FleetMember[];
- outDir: string;
- files: string[];
-}
-
-/**
- * Build the view and write `fleet.md` + `fleet.json` to `/reports/`.
- */
-export async function writeFleetView(
- dir: string,
- options: WriteViewOptions = {},
-): Promise {
- const root = resolve(dir);
- const outDir = resolve(root, options.outDir ?? FLEET_REPORTS_DIRNAME);
-
- const { view, members } = await buildFleetView(root, options);
-
- await mkdir(outDir, { recursive: true });
-
- const mdPath = join(outDir, FLEET_FILENAME);
- const jsonPath = join(outDir, FLEET_JSON_FILENAME);
-
- await writeFile(mdPath, renderFleetMarkdown(view), "utf-8");
- await writeFile(jsonPath, renderFleetJson(view), "utf-8");
-
- return {
- view,
- members,
- outDir,
- files: [FLEET_FILENAME, FLEET_JSON_FILENAME],
- };
-}
diff --git a/packages/ghost-fleet/src/skill-bundle/SKILL.md b/packages/ghost-fleet/src/skill-bundle/SKILL.md
deleted file mode 100644
index 240e94fc..00000000
--- a/packages/ghost-fleet/src/skill-bundle/SKILL.md
+++ /dev/null
@@ -1,79 +0,0 @@
----
-name: ghost-fleet
-description: Reason about a fleet of design systems — pairwise distances, cohorts, tracks-graph, world-shape narrative. Use when the user has a directory of (map.md, fingerprint.md) members and wants to understand how they relate, where they cluster, who declares whom as reference, or how the fleet's shape should be summarized for design system leadership. Triggers on phrases like "what does our design world look like", "compare these systems as a fleet", "where do our apps cluster", "who tracks whom", "summarize the fleet", "fleet view", or whenever a `fleet/` directory with member subdirectories is being explored.
-license: Apache-2.0
-metadata:
- homepage: https://github.com/block/ghost
- cli: ghost-fleet
----
-
-# Ghost Fleet — Reasoning Over Many Members
-
-Fleet is the **elevation view** across many `(map.md, fingerprint.md)` pairs. It also reads optional scoped overlays at `fingerprints/.md`. Per-repo views answer "is this repo drifting?" Fleet answers "what does our design world look like?"
-
-This skill helps you turn the output of `ghost-fleet view` into a **world-model narrative** in the body of `fleet.md`. The CLI is the calculator: pairwise distances, group-by tables, tracks-graph. You give it the prose.
-
-## CLI verbs
-
-| Verb | Purpose |
-|---|---|
-| `ghost-fleet members ` | List registered members + freshness signal. Use to confirm coverage before view. |
-| `ghost-fleet view ` | Compute pairwise distances, group-by tables, tracks-graph; emit `fleet.md` + `fleet.json` into `/reports/`. |
-| `ghost-fleet emit skill` | Install this bundle into a host agent's skill directory. |
-
-Three verbs. Tracks-graph extraction (`tracks`), temporal aggregation (`temporal`), and group-by axis stacking are not yet implemented — the milestone is intentionally narrow. Read [references/target.md](references/target.md) for the reasoning recipe.
-
-## Inputs the CLI expects
-
-A directory shaped like this:
-
-```
-fleet/
-├── members/
-│ ├── /
-│ │ ├── map.md
-│ │ ├── fingerprint.md
-│ │ └── .ghost-sync.json # optional — surfaced as a tracks edge
-│ └── ...
-└── reports/ # written by `ghost-fleet view`
- ├── fleet.md
- └── fleet.json
-```
-
-Each member is read-only. Fleet does **not** regenerate, refresh, or fetch; fingerprints evolve by deliberate act. If a member is stale, regenerate its `fingerprint.md` in that member's repo and re-run `ghost-fleet view`.
-
-## Workflow — synthesizing fleet.md
-
-When the user asks you to "summarize the fleet" or "produce a world view":
-
-1. Run `ghost-fleet members ` to confirm coverage. Note any rows that aren't `ok` — missing or broken members are worth surfacing.
-2. Run `ghost-fleet view `. This writes `fleet.md` (frontmatter + skeleton body) and `fleet.json` (structured sidecar).
-3. Read `fleet.md`. The frontmatter gives you `members`, parent `distances`, nested `nodes`, `node_distances`, `tracks`, and `groupings` (five axes).
-4. Fill in the three required body sections:
- - `## World shape` — the broad picture. Where does the fleet center? How wide is its spread? What axes (palette, spacing, typography, surfaces) account for the largest distances? Distance is data, not blame.
- - `## Cohorts` — the cluster narrative. Read the pairwise array and the groupings. Identify natural cohorts (often: same platform, same registry, or shared design ancestry). Name them. Note outliers inside each cohort and the dimension they pull on.
- - `## Tracks` — narrative over the tracks-graph. Who declares whom as reference? Are there leaves (members nobody tracks)? Loops? Cross-cohort references that warrant attention?
-5. Re-read your draft. Confirm the frontmatter and the body do not duplicate each other. Numbers belong in frontmatter; interpretation belongs in body.
-
-For the heuristics and reasoning patterns, see [references/target.md](references/target.md).
-
-## What this milestone does not do
-
-- **Scoped governance.** Fleet reads scoped overlays and compares them as nested nodes, but it does not author or promote scoped divergences. The scan/fingerprint pipeline owns those files.
-- **Tracks-graph projection beyond the recorded edges.** Fleet emits exactly what each member declared in `.ghost-sync.json`. It does not infer transitive references.
-- **Temporal aggregation.** Per-member history aggregation (`fleet.history.json`) is deferred.
-- **Axis stacking.** `--groupby platform,registry` and similar filters are deferred to a follow-up.
-
-## Always
-
-- Treat the frontmatter as the ground truth. Distances are numbers; the body is interpretation.
-- Use member ids exactly as the CLI emits them. If a member's id changed, that's an authoring concern in the member's `map.md`, not a fleet concern.
-- Surface coverage gaps. If `ghost-fleet members` reports `missing` or `error`, name them in your narrative or in the report header — silently dropping a member is dishonest.
-- Resolve track edges to member ids when possible. The CLI surfaces whatever the member wrote (a target string, an id, a path); your narrative should clarify whether the edge points at another member or at an external reference.
-
-## Never
-
-- Never regenerate a member from inside the fleet recipe. Members are read-only inputs.
-- Never invent clusters from thin air — anchor every cohort in either the pairwise distances or a group-by axis.
-- Never write distances back into the body. Numbers go in frontmatter; the body explains them.
-- Never rename a member in the CLI's output. If the id is wrong, fix the member's `map.md` and re-run.
diff --git a/packages/ghost-fleet/src/skill-bundle/references/target.md b/packages/ghost-fleet/src/skill-bundle/references/target.md
deleted file mode 100644
index 891c0a17..00000000
--- a/packages/ghost-fleet/src/skill-bundle/references/target.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Reasoning over a fleet of targets
-
-This fragment guides the world-model narrative for a fleet whose members each have a parent fingerprint and may also have product-surface overlays under `fingerprints/.md`.
-
-The CLI hands you `fleet.md` with frontmatter populated and three empty body sections: `## World shape`, `## Cohorts`, `## Tracks`. Your job is to fill those sections with prose that's grounded in the frontmatter — never restating it.
-
-## Read the frontmatter first
-
-The frontmatter holds five structured pieces. Read all five before writing a sentence.
-
-1. `members` — id, platform, build_system, registry presence, fingerprint mtime. Confirm coverage matches what `ghost-fleet members` told you. Members in the table that aren't here mean you've got an orphan map.md or a broken fingerprint.
-
-2. `distances` — parent-only pairwise array, sorted ascending by `(a, b)`. Each entry is `{a, b, distance}` where distance is in [0, 1] roughly: the cosine-derived embedding distance between two fingerprints. Treat the numbers as *relative* — there is no universal threshold for "drifted." Look at the distribution first; use any numeric bands below only as narrative calibration, never as pass/fail gates.
-
-3. `nodes` / `node_distances` — parent and scoped fingerprint nodes. Parent ids are ``; scope ids are `/` and carry `parent_id`. Use these when asking whether checkout-like or portal-like surfaces cluster across products.
-
-4. `tracks` — directed edges from each member's `.ghost-sync.json`. The right-hand side is whatever the member declared (a member id, a target string, or a local path). You decide whether the edge resolves to another member.
-
-5. `groupings.by_platform` / `by_build_system` / `by_registry` / `by_rendering` / `by_styling` — the five axes the CLI reads from each `map.md`. These are your cohort scaffolding.
-
-6. `generated_at` — note staleness. If the timestamp is more than a few weeks old and individual `fingerprint_at` dates are even older, say so.
-
-## World shape — the elevation view
-
-Two paragraphs. Anchor each claim in a number from `distances` or a group-by table.
-
-Three questions to answer:
-
-- **Where does the fleet center?** Look at the median pairwise distance. In many fleets, a median around or below 0.10 suggests a coherent family, while a median above roughly 0.30 suggests several distinct languages coexist. Let the fleet's own spread override those examples, and don't say "tight" or "wide" without naming the median.
-- **How is it shaped?** Is the spread uniform, or are there one or two outliers pulling the mean? Look at the max distance vs the median.
-- **What account axes drive the distances?** Cross-reference the largest distances against the group-by tables. Big distances between platforms? Between registries? Between two specific members regardless of axis?
-
-Distance is data, not blame. Don't moralize. "Tidal pulls away from the Cash family on warmth and density" beats "Tidal is drifting too far."
-
-## Cohorts — the cluster narrative
-
-For the milestone, fleet does **not** emit clusters in frontmatter — clustering is a projection you compute over `distances` + `groupings`. Two valid approaches:
-
-- **Group-by-derived cohorts.** Use the axes the CLI already gave you. "All web members on shadcn" is a cohort. "All ios members" is a cohort. This is the safer narrative when the fleet is small or the axes are imbalanced.
-- **Distance-derived cohorts.** Read the pairwise array. Members whose distances to each other are below the fleet median, while distances to outsiders are above it, form a cohort. Name three or four; don't try to enumerate all of them.
-
-For each cohort, write one sentence on what binds them and one sentence on the inside outlier (if any). The outlier dimension matters more than the cohort name — readers care about what to look at next.
-
-## Tracks — narrative over the graph
-
-Read `tracks` and answer:
-
-- **Who declares whom?** Resolve edges to member ids when possible. An edge `{from: "cash-web", to: "cash-design-system"}` is informative when both ids appear in `members`; less informative when `to` is `github:org/repo` and the parent is external.
-- **Are there leaves?** Members nobody tracks. Sometimes a leaf is a parent (the canonical reference everyone else tracks); sometimes it's an orphan that nobody references. The graph alone can't distinguish — name the candidates and let the reader decide.
-- **Are there loops?** A → B → A is rare and worth flagging. It usually means two members consider each other reference, which is governance noise.
-- **Cross-cohort references?** A member from the "ios" cohort tracking a member in the "web" cohort is interesting. Note the cross-reference and which cohort it bridges.
-
-## Heuristics worth codifying
-
-- When the fleet has fewer than four members, "cohorts" is a stretch. Be honest — say "too small for cohorts; here are the pairwise distances directly."
-- When two distances are within a hair of each other (e.g., 0.15 and 0.16), don't pretend the difference matters. Ranking matters; tiny gaps don't.
-- Group-by axes can be imbalanced. If 9 of 10 members are `platform: web`, the platform axis isn't doing useful work. Switch to a different axis or fall back to distance-derived cohorts.
-- The `tracks` graph is sparse by design. Most members won't have a track target. That's fine — say so once and move on.
-
-## Always
-
-- Cite. Every claim in the body should be traceable to a number in the frontmatter or to an axis survey.
-- Use the same member ids the frontmatter uses. Don't rename.
-- Keep the partition. Numbers in frontmatter; interpretation in body.
-
-## Never
-
-- Never invent a distance you didn't see. If the pairwise array doesn't have a number, don't claim one.
-- Never replace the frontmatter with prose. The frontmatter is the artifact; the body annotates it.
-- Never call drift "bad" or "good" without a stance from the user. Fleet is a measuring tool, not a leaderboard.
diff --git a/packages/ghost-fleet/test/compute.test.ts b/packages/ghost-fleet/test/compute.test.ts
deleted file mode 100644
index fcc02d30..00000000
--- a/packages/ghost-fleet/test/compute.test.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-import { dirname, resolve } from "node:path";
-import { fileURLToPath } from "node:url";
-import { describe, expect, it } from "vitest";
-import {
- computeGroupings,
- computeNodeDistances,
- computePairwiseDistances,
- computeTracks,
-} from "../src/core/compute.js";
-import { loadMembers } from "../src/core/members.js";
-
-const HERE = dirname(fileURLToPath(import.meta.url));
-const FLEET = resolve(HERE, "fixtures/small-fleet");
-
-describe("computePairwiseDistances", () => {
- it("computes one entry per unordered pair, sorted by id", async () => {
- const members = await loadMembers(FLEET);
- const distances = computePairwiseDistances(members);
-
- // 3 members → 3 unique pairs
- expect(distances).toHaveLength(3);
-
- const pairs = distances.map((d) => `${d.a}|${d.b}`);
- expect(pairs).toEqual([
- "cash-android|cash-web",
- "cash-android|ghost-ui",
- "cash-web|ghost-ui",
- ]);
-
- for (const d of distances) {
- expect(d.distance).toBeGreaterThan(0);
- expect(d.distance).toBeLessThanOrEqual(1);
- expect(typeof d.distance).toBe("number");
- }
- });
-
- it("returns the same distance for equivalent fingerprints across calls", async () => {
- const members = await loadMembers(FLEET);
- const a = computePairwiseDistances(members);
- const b = computePairwiseDistances(members);
- expect(a).toEqual(b);
- });
-
- it("uses each member's id from map.md (not the directory basename) for the pair labels", async () => {
- const members = await loadMembers(FLEET);
- const distances = computePairwiseDistances(members);
- const ids = new Set(distances.flatMap((d) => [d.a, d.b]));
- expect(ids).toEqual(new Set(["cash-android", "cash-web", "ghost-ui"]));
- });
-});
-
-describe("computeNodeDistances", () => {
- it("computes pairwise distances across parent and scoped fingerprint nodes", async () => {
- const members = await loadMembers(FLEET);
- const distances = computeNodeDistances(members);
-
- expect(distances).toHaveLength(10);
- const pairs = distances.map((d) => `${d.a}|${d.b}`);
- expect(pairs).toContain("cash-web/accounts|cash-web/payments");
- expect(pairs).toContain("cash-web|cash-web/payments");
- expect(pairs).toContain("cash-android|cash-web/accounts");
- });
-});
-
-describe("computeGroupings", () => {
- it("groups by the five axes from each member's map.md", async () => {
- const members = await loadMembers(FLEET);
- const groupings = computeGroupings(members);
-
- expect(groupings.by_platform.web).toEqual(["cash-web", "ghost-ui"]);
- expect(groupings.by_platform.android).toEqual(["cash-android"]);
-
- expect(groupings.by_build_system.pnpm).toEqual(["cash-web", "ghost-ui"]);
- expect(groupings.by_build_system.gradle).toEqual(["cash-android"]);
-
- expect(groupings.by_registry.shadcn).toEqual(["cash-web", "ghost-ui"]);
- expect(groupings.by_registry.none).toEqual(["cash-android"]);
-
- expect(groupings.by_rendering.react).toEqual(["cash-web", "ghost-ui"]);
- expect(groupings.by_rendering.compose).toEqual(["cash-android"]);
-
- expect(groupings.by_styling.tailwind).toEqual(["cash-web", "ghost-ui"]);
- expect(groupings.by_styling.material3).toEqual(["cash-android"]);
- });
-});
-
-describe("computeTracks", () => {
- it("emits one edge per member that declares a tracks target", async () => {
- const members = await loadMembers(FLEET);
- const tracks = computeTracks(members);
- expect(tracks).toEqual([{ from: "cash-web", to: "ghost-ui" }]);
- });
-
- it("emits no edges when no members track anyone", async () => {
- const tracks = computeTracks([]);
- expect(tracks).toEqual([]);
- });
-});
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/.gitignore b/packages/ghost-fleet/test/fixtures/small-fleet/.gitignore
deleted file mode 100644
index a9a1bd38..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-reports/
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-android/fingerprint.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-android/fingerprint.md
deleted file mode 100644
index 7930f057..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-android/fingerprint.md
+++ /dev/null
@@ -1,56 +0,0 @@
----
-id: cash-android
-source: llm
-timestamp: 2026-04-26T00:00:00Z
-observation:
- personality:
- - friendly
- - confident
- resembles:
- - Material 3
- - Stripe
-decisions:
- - dimension: color-strategy
- - dimension: shape-language
-palette:
- dominant:
- - { role: primary, value: "#00d632" }
- - { role: background, value: "#ffffff" }
- neutrals:
- steps: ["#ffffff", "#eeeeee", "#1a1a1a"]
- count: 3
- semantic: []
- saturationProfile: vibrant
- contrast: high
-spacing:
- scale: [4, 8, 12, 16, 24]
- regularity: 0.9
- baseUnit: 4
-typography:
- families: ["Roboto"]
- sizeRamp: [12, 14, 16, 20, 28]
- weightDistribution: { "400": 1, "500": 1, "700": 1 }
- lineHeightPattern: normal
-surfaces:
- borderRadii: [4, 12, 28]
- shadowComplexity: layered
- borderUsage: minimal
----
-
-# Character
-
-Cash on Android leans into Material 3 elevation and shape tokens, with a
-chromatic primary that carries the brand. Type follows Roboto across the
-app.
-
-# Decisions
-
-### color-strategy
-
-Brand green as the Material primary; backgrounds stay neutral with subtle
-elevation tints.
-
-### shape-language
-
-Three-tier corner radius (4 / 12 / 28) keeps small components crisp and
-larger surfaces soft.
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-android/map.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-android/map.md
deleted file mode 100644
index 4146e40a..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-android/map.md
+++ /dev/null
@@ -1,57 +0,0 @@
----
-schema: ghost.map/v1
-id: cash-android
-repo: example/cash-android
-mapped_at: 2026-04-26
-platform: android
-languages:
- - { name: kotlin, files: 1500, share: 0.97 }
- - { name: java, files: 40, share: 0.03 }
-build_system: gradle
-package_manifests:
- - settings.gradle.kts
- - build.gradle.kts
-composition:
- frameworks:
- - { name: compose }
- rendering: compose
- styling:
- - material3
-design_system:
- paths:
- - app/src/main/kotlin/com/example/cash/theme
- entry_files:
- - app/src/main/kotlin/com/example/cash/theme/Theme.kt
- status: active
-surface_sources:
- render_strategy: static-source
- include:
- - app/src/main/kotlin/com/example/cash/ui/**
- exclude:
- - "**/build/**"
-feature_areas:
- - name: home
- paths:
- - app/src/main/kotlin/com/example/cash/ui/home
- - name: cards
- paths:
- - app/src/main/kotlin/com/example/cash/ui/cards
-orientation_files:
- - README.md
----
-
-## Identity
-
-Cash on Android, rendered with Jetpack Compose. The Material 3 theme is
-extended into a Cash-branded surface, with custom shape and color tokens.
-
-## Topology
-
-Theme tokens resolve through `Theme.kt`, which composes a custom palette
-on top of a Material 3 base. UI lives under `ui/`; build outputs under
-`build/` are excluded.
-
-## Conventions
-
-Composables follow a `Cash` prefix where they extend Material primitives.
-Tokens live in Kotlin, not XML.
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/.ghost-sync.json b/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/.ghost-sync.json
deleted file mode 100644
index aa2c381b..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/.ghost-sync.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "tracks": "ghost-ui",
- "ackedAt": "2026-04-26T00:00:00Z",
- "trackedExpressionId": "ghost-ui",
- "localExpressionId": "cash-web",
- "dimensions": {},
- "overallDistance": 0
-}
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprint.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprint.md
deleted file mode 100644
index 4b9e1e6d..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprint.md
+++ /dev/null
@@ -1,54 +0,0 @@
----
-id: cash-web
-source: llm
-timestamp: 2026-04-26T00:00:00Z
-observation:
- personality:
- - friendly
- - confident
- resembles:
- - Stripe
- - Linear
-decisions:
- - dimension: color-strategy
- - dimension: spatial-system
-palette:
- dominant:
- - { role: primary, value: "#00d632" }
- - { role: background, value: "#ffffff" }
- neutrals:
- steps: ["#ffffff", "#f5f5f5", "#1a1a1a"]
- count: 3
- semantic: []
- saturationProfile: vibrant
- contrast: high
-spacing:
- scale: [4, 8, 16, 24, 32]
- regularity: 1.0
- baseUnit: 4
-typography:
- families: ["Inter"]
- sizeRamp: [14, 16, 20, 24, 32]
- weightDistribution: { "400": 1, "700": 1 }
- lineHeightPattern: normal
-surfaces:
- borderRadii: [8, 12]
- shadowComplexity: subtle
- borderUsage: minimal
----
-
-# Character
-
-Cash's web language is friendly and confident. The signature green carries
-the brand across surfaces; spacing follows a 4px grid; type stays neutral
-to let the green do the work.
-
-# Decisions
-
-### color-strategy
-
-Brand green as the only chromatic accent. Backgrounds stay neutral.
-
-### spatial-system
-
-A strict 4px grid. The scale doubles in early steps and widens at the top.
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprints/accounts.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprints/accounts.md
deleted file mode 100644
index 37caab0c..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprints/accounts.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-extends: ../fingerprint.md
-id: cash-web-accounts
-palette:
- dominant:
- - { role: primary, value: "#00b894" }
- neutrals:
- steps: ["#ffffff", "#f5f5f5", "#1a1a1a"]
- count: 3
- semantic: []
- saturationProfile: vibrant
- contrast: high
-decisions:
- - dimension: color-strategy
----
-
-# Decisions
-
-### color-strategy
-
-Account management softens the inherited green into a calmer teal accent for
-settings and account surfaces.
-
-**Evidence:**
-- `#00b894` appears as the local account accent in `src/accounts`.
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprints/payments.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprints/payments.md
deleted file mode 100644
index bd43b8cf..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/fingerprints/payments.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-extends: ../fingerprint.md
-id: cash-web-payments
-decisions:
- - dimension: density
----
-
-# Decisions
-
-### density
-
-Payment flows keep the inherited palette but tighten the rhythm around
-transaction summaries and action rows.
-
-**Evidence:**
-- `src/payments` surfaces use the parent 4px spacing grid with compact rows.
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/map.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/map.md
deleted file mode 100644
index 0748e072..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/cash-web/map.md
+++ /dev/null
@@ -1,60 +0,0 @@
----
-schema: ghost.map/v1
-id: cash-web
-repo: example/cash-web
-mapped_at: 2026-04-26
-platform: web
-languages:
- - { name: typescript, files: 120, share: 0.95 }
- - { name: css, files: 6, share: 0.05 }
-build_system: pnpm
-package_manifests:
- - package.json
-composition:
- frameworks:
- - { name: react }
- rendering: react
- styling:
- - tailwind
-registry:
- path: registry.json
- components: 24
-design_system:
- paths:
- - src/components
- entry_files:
- - src/styles/tokens.css
- status: active
-surface_sources:
- render_strategy: static-source
- include:
- - src/components/**
- exclude:
- - "**/dist/**"
-feature_areas:
- - name: payments
- paths:
- - src/payments
- - name: accounts
- paths:
- - src/accounts
-orientation_files:
- - README.md
----
-
-## Identity
-
-Cash on the web. A consumer payments app rendered as a single-page React
-application with a shadcn-style component registry.
-
-## Topology
-
-Tokens resolve through `src/styles/tokens.css`. Components live under
-`src/components/**`; product surfaces split between `payments` and
-`accounts`. The `dist/` directory is a build output and is excluded from
-sampling.
-
-## Conventions
-
-Component files colocate their tests. Token names follow Tailwind's
-`@theme` convention. The registry is generated at build time.
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/ghost-ui/fingerprint.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/ghost-ui/fingerprint.md
deleted file mode 100644
index ccbb3408..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/ghost-ui/fingerprint.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-id: ghost-ui
-source: llm
-timestamp: 2026-04-25T00:00:00Z
-observation:
- personality:
- - monochromatic
- - editorial
- - restrained
- resembles:
- - Vercel Geist
- - Linear
-decisions:
- - dimension: color-strategy
- - dimension: shape-language
-palette:
- dominant:
- - { role: primary, value: "#1a1a1a" }
- - { role: background, value: "#ffffff" }
- neutrals:
- steps: ["#ffffff", "#f5f5f5", "#e8e8e8", "#999999", "#1a1a1a"]
- count: 5
- semantic: []
- saturationProfile: muted
- contrast: high
-spacing:
- scale: [4, 8, 12, 16, 24, 32]
- regularity: 0.95
- baseUnit: 4
-typography:
- families: ["system-ui"]
- sizeRamp: [12, 14, 16, 20, 24, 32]
- weightDistribution: { "400": 1, "600": 1, "700": 1 }
- lineHeightPattern: tight
-surfaces:
- borderRadii: [10, 14, 999]
- shadowComplexity: layered
- borderUsage: moderate
----
-
-# Character
-
-A monochromatic editorial language — color is reserved for state, the
-default surface stays achromatic. Type runs tight; pill-shaped controls
-contrast moderately rounded containers.
-
-# Decisions
-
-### color-strategy
-
-Treat hue as opt-in communication. The default theme is pure achromatic.
-
-### shape-language
-
-Pill-first for actionable controls; moderate radii for structural surfaces.
diff --git a/packages/ghost-fleet/test/fixtures/small-fleet/members/ghost-ui/map.md b/packages/ghost-fleet/test/fixtures/small-fleet/members/ghost-ui/map.md
deleted file mode 100644
index 12806048..00000000
--- a/packages/ghost-fleet/test/fixtures/small-fleet/members/ghost-ui/map.md
+++ /dev/null
@@ -1,56 +0,0 @@
----
-schema: ghost.map/v1
-id: ghost-ui
-repo: example/ghost-ui
-mapped_at: 2026-04-25
-platform: web
-languages:
- - { name: typescript, files: 200, share: 0.94 }
- - { name: css, files: 12, share: 0.06 }
-build_system: pnpm
-package_manifests:
- - package.json
-composition:
- frameworks:
- - { name: react }
- rendering: react
- styling:
- - tailwind
-registry:
- path: registry.json
- components: 49
-design_system:
- paths:
- - src/components/ui
- entry_files:
- - src/styles/main.css
- status: active
-surface_sources:
- render_strategy: static-source
- include:
- - src/components/**
- exclude:
- - "**/dist/**"
-feature_areas:
- - name: catalogue
- paths:
- - src/components/ui
-orientation_files:
- - README.md
----
-
-## Identity
-
-A reference component library — 49 UI primitives — distributed via shadcn
-registry. Editorial, monochromatic visual language.
-
-## Topology
-
-Tokens resolve through `src/styles/main.css`. Components live under
-`src/components/ui`. The registry sources its component list directly
-from this directory.
-
-## Conventions
-
-Components use the shadcn convention (one file per primitive, slot
-composition for variants).
diff --git a/packages/ghost-fleet/test/members.test.ts b/packages/ghost-fleet/test/members.test.ts
deleted file mode 100644
index 1d2768d8..00000000
--- a/packages/ghost-fleet/test/members.test.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { mkdtemp, rm } from "node:fs/promises";
-import { tmpdir } from "node:os";
-import { dirname, join, resolve } from "node:path";
-import { fileURLToPath } from "node:url";
-import { describe, expect, it } from "vitest";
-import { loadMembers, summarizeMember } from "../src/core/members.js";
-
-const HERE = dirname(fileURLToPath(import.meta.url));
-const FLEET = resolve(HERE, "fixtures/small-fleet");
-
-describe("loadMembers", () => {
- it("loads each member directory under /members/", async () => {
- const members = await loadMembers(FLEET);
- expect(members.map((m) => m.id).sort()).toEqual([
- "cash-android",
- "cash-web",
- "ghost-ui",
- ]);
- });
-
- it("parses each member's map.md frontmatter", async () => {
- const members = await loadMembers(FLEET);
- const cashWeb = members.find((m) => m.id === "cash-web");
- expect(cashWeb?.mapStatus).toBe("ok");
- expect(cashWeb?.map?.platform).toBe("web");
- expect(cashWeb?.map?.build_system).toBe("pnpm");
- expect(cashWeb?.map?.registry?.components).toBe(24);
-
- const cashAndroid = members.find((m) => m.id === "cash-android");
- expect(cashAndroid?.map?.platform).toBe("android");
- expect(cashAndroid?.map?.registry).toBeUndefined();
- });
-
- it("loads each member's fingerprint with embedding backfilled", async () => {
- const members = await loadMembers(FLEET);
- for (const member of members) {
- expect(member.fingerprintStatus).toBe("ok");
- expect(member.fingerprint).toBeDefined();
- // Embedding is the load-bearing data structure for fleet's pairwise
- // distances; it must be present (computed if missing in YAML).
- expect(member.fingerprint?.embedding.length).toBeGreaterThan(0);
- expect(typeof member.fingerprintMtime).toBe("string");
- }
- });
-
- it("loads scoped fingerprint overlays as nested nodes", async () => {
- const members = await loadMembers(FLEET);
- const cashWeb = members.find((m) => m.id === "cash-web");
-
- expect(cashWeb?.fingerprintNodes.map((node) => node.id).sort()).toEqual([
- "cash-web",
- "cash-web/accounts",
- "cash-web/payments",
- ]);
- const payments = cashWeb?.fingerprintNodes.find(
- (node) => node.id === "cash-web/payments",
- );
- expect(payments?.kind).toBe("scope");
- expect(payments?.scopeId).toBe("payments");
- expect(payments?.parentId).toBe("cash-web");
- expect(payments?.fingerprint.id).toBe("cash-web-payments");
- });
-
- it("surfaces tracks targets from .ghost-sync.json when present", async () => {
- const members = await loadMembers(FLEET);
- const cashWeb = members.find((m) => m.id === "cash-web");
- const ghostUi = members.find((m) => m.id === "ghost-ui");
- expect(cashWeb?.tracks).toBe("ghost-ui");
- // ghost-ui has no .ghost-sync.json → tracks is undefined
- expect(ghostUi?.tracks).toBeUndefined();
- });
-
- it("returns an empty list when the directory has no member subdirectories", async () => {
- const tmp = await mkdtemp(join(tmpdir(), "ghost-fleet-empty-"));
- try {
- const members = await loadMembers(tmp);
- expect(members).toEqual([]);
- } finally {
- await rm(tmp, { recursive: true, force: true });
- }
- });
-});
-
-describe("summarizeMember", () => {
- it("flattens a member into the freshness row", async () => {
- const members = await loadMembers(FLEET);
- const cashWeb = members.find((m) => m.id === "cash-web");
- if (!cashWeb) throw new Error("missing cash-web");
- const summary = summarizeMember(cashWeb);
- expect(summary.id).toBe("cash-web");
- expect(summary.platform).toBe("web");
- expect(summary.build_system).toBe("pnpm");
- expect(summary.registry).toBe("registry.json");
- expect(summary.ok).toBe(true);
- expect(summary.fingerprint_mtime).toMatch(/^\d{4}-\d{2}-\d{2}/);
- });
-
- it("renders 'none' as null for non-shadcn members", async () => {
- const members = await loadMembers(FLEET);
- const cashAndroid = members.find((m) => m.id === "cash-android");
- if (!cashAndroid) throw new Error("missing cash-android");
- const summary = summarizeMember(cashAndroid);
- expect(summary.registry).toBeNull();
- });
-});
diff --git a/packages/ghost-fleet/test/view.test.ts b/packages/ghost-fleet/test/view.test.ts
deleted file mode 100644
index 5332ebfb..00000000
--- a/packages/ghost-fleet/test/view.test.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import { mkdtemp, readFile, rm } from "node:fs/promises";
-import { tmpdir } from "node:os";
-import { dirname, join, resolve } from "node:path";
-import { fileURLToPath } from "node:url";
-import { afterEach, beforeEach, describe, expect, it } from "vitest";
-import { parse as parseYaml } from "yaml";
-import {
- FLEET_FILENAME,
- FLEET_JSON_FILENAME,
- FleetFrontmatterSchema,
- REQUIRED_BODY_SECTIONS,
-} from "../src/core/schema.js";
-import {
- buildFleetView,
- renderFleetJson,
- renderFleetMarkdown,
- writeFleetView,
-} from "../src/core/view.js";
-
-const HERE = dirname(fileURLToPath(import.meta.url));
-const FLEET = resolve(HERE, "fixtures/small-fleet");
-
-describe("buildFleetView", () => {
- it("returns the canonical FleetView shape", async () => {
- const { view } = await buildFleetView(FLEET, {
- now: new Date("2026-04-27T00:00:00Z"),
- id: "small-fleet",
- });
-
- expect(view.schema).toBe("ghost.fleet/v1");
- expect(view.id).toBe("small-fleet");
- expect(view.generated_at).toBe("2026-04-27");
- expect(view.members.map((m) => m.id).sort()).toEqual([
- "cash-android",
- "cash-web",
- "ghost-ui",
- ]);
- expect(view.distances).toHaveLength(3);
- expect(view.nodes.map((node) => node.id).sort()).toEqual([
- "cash-android",
- "cash-web",
- "cash-web/accounts",
- "cash-web/payments",
- "ghost-ui",
- ]);
- expect(view.node_distances).toHaveLength(10);
- expect(view.tracks).toEqual([{ from: "cash-web", to: "ghost-ui" }]);
- expect(Object.keys(view.groupings).sort()).toEqual([
- "by_build_system",
- "by_platform",
- "by_registry",
- "by_rendering",
- "by_styling",
- ]);
- });
-
- it("emits a frontmatter shape that validates against FleetFrontmatterSchema", async () => {
- const { view } = await buildFleetView(FLEET, {
- now: new Date("2026-04-27T00:00:00Z"),
- id: "small-fleet",
- });
- // Composition: view → frontmatter, run through the schema. This is
- // the same shape the lint verb (when added) would gate on.
- const result = FleetFrontmatterSchema.safeParse(view);
- expect(result.success).toBe(true);
- });
-});
-
-describe("renderFleetJson", () => {
- it("serializes the view as stable JSON ending in a newline", async () => {
- const { view } = await buildFleetView(FLEET, {
- now: new Date("2026-04-27T00:00:00Z"),
- id: "small-fleet",
- });
- const json = renderFleetJson(view);
- expect(json.endsWith("\n")).toBe(true);
- const parsed = JSON.parse(json);
- expect(parsed.schema).toBe("ghost.fleet/v1");
- expect(parsed.id).toBe("small-fleet");
- });
-});
-
-describe("renderFleetMarkdown", () => {
- it("emits a frontmatter block plus the three required body section headings", async () => {
- const { view } = await buildFleetView(FLEET, {
- now: new Date("2026-04-27T00:00:00Z"),
- id: "small-fleet",
- });
- const md = renderFleetMarkdown(view);
- expect(md.startsWith("---\n")).toBe(true);
- for (const section of REQUIRED_BODY_SECTIONS) {
- expect(md).toContain(`## ${section}`);
- }
- // Frontmatter block must be parseable YAML and validate against the schema.
- const fmMatch = /^---\n([\s\S]*?)\n---/.exec(md);
- expect(fmMatch).not.toBeNull();
- const fm = parseYaml(fmMatch?.[1] ?? "");
- const result = FleetFrontmatterSchema.safeParse(fm);
- expect(result.success).toBe(true);
- });
-});
-
-describe("writeFleetView", () => {
- let tmp: string;
-
- beforeEach(async () => {
- tmp = await mkdtemp(join(tmpdir(), "ghost-fleet-test-"));
- });
-
- afterEach(async () => {
- await rm(tmp, { recursive: true, force: true });
- });
-
- it("writes fleet.md and fleet.json into /reports/", async () => {
- const result = await writeFleetView(FLEET, {
- outDir: tmp,
- now: new Date("2026-04-27T00:00:00Z"),
- id: "small-fleet",
- });
- expect(result.files).toEqual([FLEET_FILENAME, FLEET_JSON_FILENAME]);
-
- const md = await readFile(join(tmp, FLEET_FILENAME), "utf-8");
- expect(md).toContain("schema: ghost.fleet/v1");
- expect(md).toContain("## World shape");
-
- const json = JSON.parse(
- await readFile(join(tmp, FLEET_JSON_FILENAME), "utf-8"),
- );
- expect(json.id).toBe("small-fleet");
- expect(json.distances).toHaveLength(3);
- expect(json.nodes).toHaveLength(5);
- expect(json.node_distances).toHaveLength(10);
- });
-});
diff --git a/packages/ghost-fleet/tsconfig.json b/packages/ghost-fleet/tsconfig.json
deleted file mode 100644
index f4e64d23..00000000
--- a/packages/ghost-fleet/tsconfig.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "extends": "../../tsconfig.json",
- "compilerOptions": {
- "composite": true,
- "outDir": "./dist",
- "rootDir": "./src"
- },
- "include": ["src"],
- "references": [{ "path": "../ghost" }]
-}
diff --git a/packages/ghost-ui/.ghost/intent.yml b/packages/ghost-ui/.ghost/intent.yml
deleted file mode 100644
index 3122b7b9..00000000
--- a/packages/ghost-ui/.ghost/intent.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-summary:
- product: Ghost UI
- audience:
- - Agents and engineers composing AI/product interfaces from the Ghost UI registry.
- goals:
- - Provide reusable building blocks of tokens, primitives, AI elements, and interaction conventions.
- anti_goals:
- - Define product-specific flows, copy, trust obligations, or business intent for consuming apps.
-situations: []
-principles: []
-experience_contracts: []
diff --git a/packages/ghost-ui/.ghost/inventory.yml b/packages/ghost-ui/.ghost/inventory.yml
deleted file mode 100644
index 348962cd..00000000
--- a/packages/ghost-ui/.ghost/inventory.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-topology:
- scopes:
- - id: ui-primitives
- paths: [src/components/ui]
- surface_types: [component-library]
- - id: ai-elements
- paths: [src/components/ai-elements]
- surface_types: [component-library]
- - id: tokens
- paths: [src/styles]
- surface_types: [token-system]
- - id: registry
- paths: [registry.json, public/r/registry.json]
- surface_types: [shadcn-registry]
-building_blocks:
- tokens:
- - src/styles/main.css
- components:
- - src/components/ui
- - src/components/ai-elements
- - public/r/registry.json
- - registry.json
- libraries:
- - shadcn-registry
- - radix-ui
- - lucide-react
- - tailwindcss
- assets:
- - src/fonts
- notes:
- - Ghost UI is registry-distributed implementation inventory, not an installable npm package or consuming-product experience memory.
-exemplars: []
-sources: []
diff --git a/packages/ghost-ui/README.md b/packages/ghost-ui/README.md
deleted file mode 100644
index e4d8c2c9..00000000
--- a/packages/ghost-ui/README.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# ghost-ui
-
-**Reference design system for the Ghost project. 97 components, shadcn registry, not published to npm.**
-
-`ghost-ui` is the reference component system Ghost uses to exercise registry
-and agent-integration workflows. It's distributed as a generated shadcn registry
-(`public/r/registry.json`) for drop-in consumption, not as an npm package. If you're
-looking for the fingerprint capture and drift-review tool, that's
-[`@anarchitecture/ghost`](../ghost).
-
-## Registry convention
-
-This workspace carries a repo-local Ghost reference bundle in `.ghost/`.
-`.ghost/intent.yml` and `.ghost/inventory.yml` describe Ghost UI as implementation vocabulary: tokens,
-component families, registry shape, and reference-registry boundaries. It does
-not define product-specific flows, copy, trust obligations, or business intent
-for consuming apps. New products should reference this bundle and the generated
-`public/r/registry.json`, then fill their own surface-composition fingerprint
-separately.
-
-Agents should read this README, `.ghost/`,
-`public/r/registry.json`, `registry.json`, `.shadcn/skills.md`, and source files
-when integrating components.
-
-The shadcn registry entries can carry opportunistic, namespaced item metadata:
-
-- **`meta.fingerprint_dimensions`** per item — declares which embedding dimensions a component primarily expresses (`palette`, `spacing`, `typography`, `surfaces`). Drift tooling can use this for higher-confidence per-component attribution; absent the field, consumers fall back to file content and registry categories.
-
-Shape-aware examples can add two more optional `meta` fields:
-
-- **`meta.exemplar_kind`** — `atom` for primitive controls such as badge, button, cell, or input; `shape` for composed outputs.
-- **`meta.response_shapes`** — the composed shape(s) an example demonstrates: `article`, `tracker`, `comparison`, or `card`.
-
-That distinction helps generators pick relevant references instead of treating every example as a card. `card` is one response shape; it is not the default form of all intelligence.
-
-## What's here
-
-- **Components** — 49 UI primitives (Radix-based) + 48 AI elements (chat, streaming, agent UI) + theme + hooks.
-- **Tokens** — `src/styles/` CSS custom properties consumed by the registry and components.
-- **Registry** — `public/r/registry.json`, generated shadcn-compatible catalogue for consumption. Source entries live in `registry.json`; rebuilt by `just build-registry`.
-- **Ghost reference context** — `.ghost/`, used as reference-registry context by consuming products.
-- **Agent context** — `.shadcn/skills.md`, generated from the registry and component sources for AI assistants.
-
-## Use
-
-Consume via the shadcn registry (the intended path — not npm):
-
-```bash
-npx shadcn add /
-```
-
-Or build the library locally for workspace linking:
-
-```bash
-pnpm --filter ghost-ui build:lib
-```
-
-See [`apps/docs`](../../apps/docs) for the live component catalogue.
-
-## MCP server
-
-`ghost-ui` also ships a `ghost-mcp` bin — a Model Context Protocol server that re-exposes the component registry to AI assistants (Claude Code, Cursor, etc.) so they can discover and install components without a human in the loop. 5 tools, 2 resources. Source lives in `src/mcp/`, built separately via `tsconfig.mcp.json` → `dist-mcp/`.
-
-```bash
-pnpm --filter ghost-ui build:mcp
-node packages/ghost-ui/dist-mcp/bin.js # stdio server
-```
-
-Wire it into your MCP host by pointing at the `ghost-mcp` bin.
-
-## License
-
-Apache-2.0
diff --git a/packages/ghost-ui/src/components/ui/calendar.tsx b/packages/ghost-ui/src/components/ui/calendar.tsx
deleted file mode 100644
index 529c6d46..00000000
--- a/packages/ghost-ui/src/components/ui/calendar.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-"use client";
-
-import { ChevronLeft, ChevronRight } from "lucide-react";
-import type * as React from "react";
-import { DayPicker } from "react-day-picker";
-import { buttonVariants } from "@/components/ui/button";
-import { cn } from "@/lib/utils";
-
-function Calendar({
- className,
- classNames,
- showOutsideDays = true,
- ...props
-}: React.ComponentProps) {
- return (
- .day-range-start)]:[background:linear-gradient(to_right,transparent_50%,var(--color-background-muted)_50%)] [&:has(>.day-range-end)]:[background:linear-gradient(to_left,transparent_50%,var(--color-background-muted)_50%)]"
- : "[&:has([aria-selected])]:rounded-full [&:has([aria-selected])]:bg-muted",
- ),
- day_button: cn(
- buttonVariants({ variant: "ghost" }),
- "relative z-10 size-8 p-0 font-normal aria-selected:opacity-100",
- ),
- range_start:
- "day-range-start rounded-full aria-selected:bg-primary aria-selected:text-primary-foreground",
- range_end:
- "day-range-end rounded-full aria-selected:bg-primary aria-selected:text-primary-foreground",
- selected:
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
- today: "bg-muted text-muted-foreground",
- outside:
- "day-outside text-muted-foreground aria-selected:text-muted-foreground",
- disabled: "text-muted-foreground opacity-50",
- range_middle:
- "aria-selected:bg-muted aria-selected:text-muted-foreground",
- hidden: "invisible",
- ...classNames,
- }}
- components={{
- Chevron: ({ orientation, ...props }) => {
- if (orientation === "left") {
- return ;
- }
- return ;
- },
- }}
- {...props}
- />
- );
-}
-
-export { Calendar };
diff --git a/packages/ghost-ui/src/components/ui/tabs.tsx b/packages/ghost-ui/src/components/ui/tabs.tsx
deleted file mode 100644
index 88ea141c..00000000
--- a/packages/ghost-ui/src/components/ui/tabs.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-"use client";
-
-import * as TabsPrimitive from "@radix-ui/react-tabs";
-import type * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-function Tabs({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function TabsList({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function TabsTrigger({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function TabsContent({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-export { Tabs, TabsContent, TabsList, TabsTrigger };
diff --git a/packages/ghost-ui/src/components/ui/textarea.tsx b/packages/ghost-ui/src/components/ui/textarea.tsx
deleted file mode 100644
index 924524bf..00000000
--- a/packages/ghost-ui/src/components/ui/textarea.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import type * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
- return (
-
- );
-}
-
-export { Textarea };
diff --git a/packages/ghost/CHANGELOG.md b/packages/ghost/CHANGELOG.md
index e0acdfc9..252f74a8 100644
--- a/packages/ghost/CHANGELOG.md
+++ b/packages/ghost/CHANGELOG.md
@@ -1,4 +1,4 @@
-# @anarchitecture/ghost
+# @design-intelligence/ghost
## 0.18.1
diff --git a/packages/ghost/README.md b/packages/ghost/README.md
index bdef9c74..82db75cc 100644
--- a/packages/ghost/README.md
+++ b/packages/ghost/README.md
@@ -1,109 +1,74 @@
-# @anarchitecture/ghost
+# @design-intelligence/ghost
-**A unified Ghost CLI for product-surface composition fingerprints.**
+**Your brand, packed for agents: a portable steering packet of prose truths,
+checked into the repo, read before anything is made and reviewable after.**
-Ghost captures the composition of a product surface: the intent behind it, the
-materials it draws from, and the patterns that make it feel intentional. It
-stores that composition in a repo-local `.ghost/` package that host
-agents can read before generation and validate after changes.
+This package ships one CLI: `ghost`. Every command is also available as
+`ghost-fingerprint` for when another tool owns the `ghost` bin.
-This package ships one CLI: `ghost`.
-
-## Project Status: Beta
-
-> [!WARNING]
-> Ghost is pre-1.0 and under active development. The CLI, fingerprint schema,
-> on-disk `.ghost/` package shape, and public JavaScript exports may
-> change in breaking ways before a stable 1.0 release.
->
-> Breaking changes may ship in minor versions while Ghost is pre-1.0. Patch
-> versions are reserved for fixes that should not require migration. If you adopt
-> Ghost today, expect some churn, pin the version you depend on, and review
-> release notes before upgrading.
+The scope is the thesis: the few still write the taste; they no longer gate it.
+You hand the brand over as a packet of authority that travels to wherever work
+ships.
## Install
```bash
-npm install -D @anarchitecture/ghost
+npm install -D @design-intelligence/ghost
npx ghost --help
-npx ghost --help --all
```
-`ghost --help` shows the core workflow. `ghost --help --all` shows the complete
-command index.
-
-## Use
-
-Create and validate the fingerprint package:
+## Shape
-```bash
-ghost init
-ghost scan --format json
-ghost lint .ghost
-ghost verify .ghost --root .
+```text
+.ghost/
+ manifest.yml # schema + package id
+ glossary.md # category vocabulary
+ index.md # front door node
+ principle.trust.md # prose truth
+ asset.logo.md # prose truth, optionally with materials
+ checks/ # optional review assertions; never gathered as nodes
```
-Gather context before generation:
-
-```bash
-ghost relay gather apps/checkout/review/page.tsx
-```
+A node is markdown with `description`, optional `materials`, and a prose body.
+`materials` accepts repo-relative paths/globs and HTTPS URLs.
-Govern changes afterward:
+Checks live under `.ghost/checks/` (scaffold with `ghost checks init`) and
+declare `references` to the nodes they review.
+`ghost review` reads a diff, matches touched files to node materials, offers
+relevant checks, and emits an advisory packet for the host agent.
-```bash
-ghost check --base main
-ghost review --base main
-```
-
-Install the BYOA skill bundle so your host agent can author, brief, review,
-verify, remediate, and update fingerprints:
+## Use
```bash
-ghost skill install
+ghost init
+ghost checks init
+ghost validate
+ghost gather "checkout settings"
+ghost pull principle.trust
+ghost review --diff=-
+ghost pulse
```
-Advanced commands such as `signals`, `stack`, `compare`, `ack`, `track`, and
-`diverge` remain available in the full command index.
-
-Zero config for every verb. No API key is required. `OPENAI_API_KEY` /
-`VOYAGE_API_KEY` are optional and only used by semantic embedding helpers when a
-host opts in.
+`ghost manifest` emits a self-describing JSON index of commands and flags.
+`ghost skill install` installs the host-agent skill bundle.
## Library
```ts
-import { compare } from "@anarchitecture/ghost/compare";
-import { runGhostCheck } from "@anarchitecture/ghost/govern";
-import { gatherRelayContext } from "@anarchitecture/ghost/relay";
import {
initFingerprintPackage,
lintFingerprintPackage,
- verifyFingerprintPackage,
-} from "@anarchitecture/ghost/fingerprint";
+ loadFingerprintPackage,
+} from "@design-intelligence/ghost/fingerprint";
+import { buildCatalogMenu } from "@design-intelligence/ghost/core";
+import { buildCli } from "@design-intelligence/ghost/cli";
```
-## BYOA
-
-Ghost is bring-your-own-agent. The CLI performs deterministic work: repo
-signals, readiness reporting, linting, verification, comparison, checks, and
-advisory review packet generation. The installed `ghost` skill teaches a host
-agent how to capture canonical `.ghost/` surface-composition
-context, brief and generate work from it, review changes against it, verify
-generated UI, remediate issues, and suggest fingerprint edits when the user
-asks.
-
-```text
-Set up the Ghost fingerprint for this repo.
-Brief this work from the Ghost fingerprint.
-Review this PR against the Ghost fingerprint.
-```
-
-## Maintainers
-
-npm renders this package-local `README.md`, not the monorepo root README. The
-npm package page updates only when a new package version is published, so
-README-only changes still need a patch changeset and release.
+Available subpath exports: `@design-intelligence/ghost`,
+`@design-intelligence/ghost/fingerprint`,
+`@design-intelligence/ghost/core`,
+`@design-intelligence/ghost/cli`, and
+`@design-intelligence/ghost/scan`.
## License
diff --git a/packages/ghost/package.json b/packages/ghost/package.json
index db297bb5..6c31c452 100644
--- a/packages/ghost/package.json
+++ b/packages/ghost/package.json
@@ -1,7 +1,7 @@
{
- "name": "@anarchitecture/ghost",
- "version": "0.18.1",
- "description": "Unified Ghost CLI for product-surface composition fingerprints, deterministic checks, advisory review, and comparison",
+ "name": "@design-intelligence/ghost",
+ "version": "0.19.0",
+ "description": "Ghost CLI for the brand fingerprint: a portable steering packet of prose truths your agent reads before it builds, with advisory review after",
"license": "Apache-2.0",
"author": "Block, Inc.",
"repository": {
@@ -26,7 +26,8 @@
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
- "ghost": "./dist/bin.js"
+ "ghost": "./dist/bin.js",
+ "ghost-fingerprint": "./dist/bin.js"
},
"imports": {
"#ghost-core": {
@@ -51,22 +52,6 @@
"types": "./dist/fingerprint.d.ts",
"import": "./dist/fingerprint.js"
},
- "./govern": {
- "types": "./dist/govern.d.ts",
- "import": "./dist/govern.js"
- },
- "./compare": {
- "types": "./dist/compare.d.ts",
- "import": "./dist/compare.js"
- },
- "./relay": {
- "types": "./dist/relay.d.ts",
- "import": "./dist/relay.js"
- },
- "./drift": {
- "types": "./dist/core/index.d.ts",
- "import": "./dist/core/index.js"
- },
"./scan": {
"types": "./dist/scan/index.d.ts",
"import": "./dist/scan/index.js"
@@ -84,13 +69,11 @@
"provenance": true
},
"scripts": {
- "build": "rm -rf dist && tsc --build --force && chmod +x dist/bin.js && node ../../scripts/link-package-bin.mjs && cp -r src/skill-bundle dist/skill-bundle",
+ "build": "rm -rf dist tsconfig.tsbuildinfo && tsc --build --force && chmod +x dist/bin.js && node ../../scripts/link-package-bin.mjs && cp -r src/skill-bundle dist/skill-bundle",
"prepublishOnly": "pnpm build"
},
"dependencies": {
"cac": "^6.7.14",
- "jiti": "^2.4.0",
- "tinyglobby": "^0.2.16",
"yaml": "^2.8.3",
"zod": "^4.3.6"
}
diff --git a/packages/ghost/src/cli.ts b/packages/ghost/src/cli.ts
index 94ef11da..665f4402 100644
--- a/packages/ghost/src/cli.ts
+++ b/packages/ghost/src/cli.ts
@@ -1,324 +1,36 @@
-import { execFile } from "node:child_process";
-import { readFileSync } from "node:fs";
-import { readFile } from "node:fs/promises";
-import { dirname, resolve } from "node:path";
-import { fileURLToPath } from "node:url";
-import { promisify } from "node:util";
import { cac } from "cac";
-import { formatGhostHelp } from "./command-discovery.js";
-import { loadComparableFingerprint } from "./comparable-fingerprint.js";
-import {
- compare,
- formatComparison,
- formatComparisonJSON,
- formatCompositeComparison,
- formatCompositeComparisonJSON,
- formatGhostDriftCheckMarkdown,
- formatTemporalComparison,
- formatTemporalComparisonJSON,
- readHistory,
- readSyncManifest,
- runGateCli,
- runGhostDriftCheck,
-} from "./core/index.js";
-import { registerDriftCommand } from "./drift-command.js";
-import {
- registerAckCommand,
- registerDivergeCommand,
- registerTrackCommand,
-} from "./evolution-commands.js";
-import { formatSemanticDiff } from "./fingerprint.js";
-import { registerFingerprintCommands } from "./fingerprint-commands.js";
-import { registerRelayCommand } from "./relay-command.js";
-import {
- buildReviewPacket,
- formatReviewPacketMarkdown,
-} from "./review-packet.js";
-import { registerSkillCommand } from "./skill-command.js";
-
-const execFileAsync = promisify(execFile);
-
-export { getCommandDiscoveryMetadata } from "./command-discovery.js";
+import { registerChecksCommand } from "./commands/checks-command.js";
+import { formatGhostHelp } from "./commands/command-discovery.js";
+import { registerExportCommand } from "./commands/export-command.js";
+import { registerFingerprintCommands } from "./commands/fingerprint-commands.js";
+import { registerGatherCommand } from "./commands/gather-command.js";
+import { registerManifestCommand } from "./commands/manifest-command.js";
+import { registerPullCommand } from "./commands/pull-command.js";
+import { registerPulseCommand } from "./commands/pulse-command.js";
+import { registerReviewCommand } from "./commands/review-command.js";
+import { registerSkillCommand } from "./commands/skill-command.js";
+import { readPackageVersion } from "./package-version.js";
+
+export {
+ buildCliManifest,
+ getCommandDiscoveryMetadata,
+} from "./commands/command-discovery.js";
export function buildCli(): ReturnType {
const cli = cac("ghost");
registerFingerprintCommands(cli);
-
- // --- compare ---
- cli
- .command(
- "compare [...fingerprints]",
- "Compare two or more fingerprints or root .ghost bundles. N=2 returns a pairwise delta; N≥3 returns a composite fingerprint.",
- )
- .option("--semantic", "Qualitative diff of decisions + palette (N=2 only)")
- .option(
- "--temporal",
- "Add velocity, trajectory, and ack bounds (N=2, reads .ghost/history.jsonl)",
- )
- .option(
- "--history-dir ",
- "Directory containing .ghost/history.jsonl (for --temporal, defaults to cwd)",
- )
- .option(
- "--gate",
- "Reconcile against a sync manifest and emit a structured pass/fail verdict (N=2 only)",
- )
- .option(
- "--sync ",
- "Sync manifest path for --gate (default: ./.ghost-sync.json)",
- )
- .option(
- "--max-divergence-days ",
- "For --gate: flag diverging dimensions older than this many days as uncovered",
- )
- .option("--format ", "Output format: cli or json", { default: "cli" })
- .action(async (fingerprints: string[], opts) => {
- try {
- if (opts.gate) {
- await runGateCli({
- fingerprints,
- cwd: process.cwd(),
- sync: opts.sync,
- format: opts.format,
- maxDivergenceDays: opts.maxDivergenceDays,
- loadFingerprint: loadComparableFingerprint,
- compare,
- });
- return;
- }
-
- const exprs = await Promise.all(
- fingerprints.map((path) => loadComparableFingerprint(path)),
- );
-
- let history: Awaited