Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/adapting-a-starter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@design-intelligence/ghost": minor
---

The skill bundle gains the `adapting-a-starter` recipe: the seven-step transplant procedure for turning an installed starter — a body like vessel-light or the naked skeleton — into your own brand's fingerprint, plus guidance for consuming an unadapted starter honestly.
5 changes: 5 additions & 0 deletions .changeset/body-install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@design-intelligence/ghost": minor
---

`ghost init` gains `--body vessel-light`: install a full inhabited fingerprint package — answered signature dials, a materials tree with tokens and embedded fonts, annotated reference compositions, and its own checks. Init template payloads now ship as packed files in dist, so bodies can carry binary materials.
5 changes: 5 additions & 0 deletions .changeset/median-headings-vessel-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@design-intelligence/ghost": patch
---

Reframe the median as Ghost-stamped model truth and align Vessel's median check with heading anchors.
5 changes: 5 additions & 0 deletions .changeset/median-one-home.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@design-intelligence/ghost": minor
---

The median floor gets one authored home: `anti-goal.median` rules become markdown headings with per-rule check references, so `ghost validate` warns on every flag orphaned by pruning. All init templates (`skeleton`, `minimal`, `composition`) now stamp the median node; `ghost checks init` copies the paired check from the packaged payload and skips it when the node is absent.
5 changes: 5 additions & 0 deletions .changeset/review-locator-matching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@design-intelligence/ghost": patch
---

`ghost review` now resolves package-relative `materials/…` locators to their repo-relative form before matching diff paths, so packages living below the repo root offer their value checks correctly.
5 changes: 5 additions & 0 deletions .changeset/skeleton-default-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@design-intelligence/ghost": minor
---

`ghost init` now scaffolds the skeleton starter: value-free grammar nodes, unanswered signature dials, and the `anti-goal.median` floor — replacing the Morrow Ledger demo content. `ghost checks init` scaffolds a live median-tells check alongside the example. `steering` remains as an alias for the new `skeleton` template.
19 changes: 18 additions & 1 deletion apps/docs/src/content/docs/cli-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,29 @@ a finding (exit `1`).

### Initialize: `init`

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
Scaffold a `.ghost/` package. Every template and body includes `anti-goal.median`; the skeleton starter also includes a manifest, a `glossary.md` declaring the grammar/signature/register/anti-goal kinds, a core `index.md`, six grammar nodes of value-free decision logic, and four signature nodes as unanswered dials awaiting your brand's values. The median node is Ghost's measured knowledge of unsteered model behavior — model truth, not brand truth — stamped at init and owned (pruned, adapted) by you thereafter. Nothing in the skeleton is fiction: grammar and the median floor are safe to consume verbatim; the dials tell the agent to ask or flag, never freehand. Use `--template minimal` when you only want the small manifest/glossary/index starter. Add truths by adding files (`principle.density.md`). Use
`--package <dir>` 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.

Templates are shapes of emptiness — structures awaiting your truths. A
**body** is the same anatomy inhabited: answered signature dials, a
`materials/` tree (tokens, fonts, worked reference compositions), and its own
`checks/`. Install one with `--body`:

```bash
ghost init --body vessel-light
```

`vessel-light` installs Vessel's design language at full strength — the
fork-axis corpus, `materials/tokens.css` owning every literal value, embedded
fonts, six annotated refs, and five checks, included automatically (no
`--with checks` needed; combining them is an error). The manifest id stays
`vessel-light` until you adapt the starter: renaming it is deliberately the
first step of making the brand yours. `--body` and `--template` are mutually
exclusive.

<CliHelp tool="ghost" command="init" hideDescription />

```bash
Expand Down
16 changes: 14 additions & 2 deletions apps/docs/src/content/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ assembles advisory review packets after, but never grades output itself. You
work through a short loop:

```bash
ghost init # scaffold .ghost/ with the steering starter
ghost init # scaffold .ghost/ with the skeleton starter
ghost validate # artifact shape + node validity + kind-prefix check
ghost gather <ask> # emit the menu for this task; your agent selects the truths it needs
ghost pull <ids> # read the selected truths' full bodies
Expand Down Expand Up @@ -80,13 +80,25 @@ 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.
and asking you to curate the claims. `ghost init` stamps `anti-goal.median` into every template and body; the median node is Ghost's measured knowledge of unsteered model behavior — model truth, not brand truth — owned (pruned, adapted) by you after init. The skeleton starter also includes `manifest.yml`, a `glossary.md` declaring the grammar/signature/register/anti-goal kinds, a core `index.md`, six grammar nodes of value-free decision logic, and four signature nodes as unanswered dials. Nothing in the skeleton is fiction: grammar and the median floor hold for any brand, and each dial tells the agent to ask you or flag the gap rather than freehand a value. Answering the dials is how the skeleton becomes your brand. Use `ghost init --template minimal` when you only want the small manifest/glossary/index starter.

```bash
ghost init
ghost validate
```

Prefer starting from a working brand instead of an empty anatomy? Install a
**body** — the same skeleton with a real brand's values plugged in: answered
dials, a `materials/` tree with tokens and fonts, worked reference
compositions, and its own checks:

```bash
ghost init --body vessel-light
```

The body's manifest keeps its own id (`vessel-light`) until you adapt it —
renaming the id is deliberately the first step of making the brand yours.

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,
Expand Down
12 changes: 10 additions & 2 deletions apps/docs/src/generated/cli-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-07-07T16:48:56.039Z",
"generatedAt": "2026-07-09T11:06:22.019Z",
"tools": [
{
"tool": "ghost",
Expand Down Expand Up @@ -53,7 +53,15 @@
{
"rawName": "--template <name>",
"name": "template",
"description": "Init template to scaffold (default: steering)",
"description": "Init template to scaffold (default: skeleton)",
"default": null,
"takesValue": true,
"negated": false
},
{
"rawName": "--body <name>",
"name": "body",
"description": "Init body to install: a full inhabited package (e.g. vessel-light)",
"default": null,
"takesValue": true,
"negated": false
Expand Down
1 change: 1 addition & 0 deletions install/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"files": [
"SKILL.md",
"references/adapting-a-starter.md",
"references/authoring-scenarios.md",
"references/blocks.md",
"references/brief.md",
Expand Down
2 changes: 1 addition & 1 deletion packages/ghost/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"provenance": true
},
"scripts": {
"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",
"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 && node ../../scripts/sync-init-payloads.mjs",
"prepublishOnly": "pnpm build"
},
"dependencies": {
Expand Down
9 changes: 8 additions & 1 deletion packages/ghost/src/commands/checks-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ export function registerChecksCommand(cli: CAC): void {
if (opts.format === "json") {
process.stdout.write(
`${JSON.stringify(
{ dir: result.dir, written: result.written },
{
dir: result.dir,
written: result.written,
skipped: result.skipped,
},
null,
2,
)}\n`,
Expand All @@ -45,6 +49,9 @@ export function registerChecksCommand(cli: CAC): void {
for (const file of result.written) {
process.stdout.write(` ${file}\n`);
}
for (const file of result.skipped) {
process.stdout.write(` skipped ${file}\n`);
}
}
process.exit(0);
} catch (err) {
Expand Down
43 changes: 32 additions & 11 deletions packages/ghost/src/commands/init-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { UsageError } from "#ghost-core";
import { initFingerprintPackage } from "../fingerprint.js";
import { addChecksDir } from "../scan/check-scaffold.js";
import { resolveGhostDirDefault } from "../scan/index.js";
import { getInitBody } from "../scan/templates.js";
import { failFromError } from "./errors.js";

export function registerInitCommand(cli: CAC): void {
Expand All @@ -14,7 +15,11 @@ export function registerInitCommand(cli: CAC): void {
)
.option(
"--template <name>",
"Init template to scaffold (default: steering)",
"Init template to scaffold (default: skeleton)",
)
.option(
"--body <name>",
"Init body to install: a full inhabited package (e.g. vessel-light)",
)
.option(
"--with <capabilities>",
Expand All @@ -35,25 +40,33 @@ export function registerInitCommand(cli: CAC): void {
typeof opts.package === "string" ? opts.package : undefined;
const ghostDir =
exactPackage === undefined ? ghostDirFromEnv() : undefined;
const withIds = parseWithCapabilities(opts.with);
for (const id of withIds) {
if (id !== "checks") {
throw new UsageError(
`Unknown --with capability '${id}'. Available: checks.`,
);
}
}
const body =
typeof opts.body === "string" ? getInitBody(opts.body) : undefined;
if (body?.includesChecks && withIds.includes("checks")) {
throw new UsageError(
`--with checks is redundant with --body ${body.name} — this body already includes its own checks/.`,
);
}

const result = await initFingerprintPackage(
exactPackage ?? ghostDir,
process.cwd(),
{
...(typeof opts.template === "string"
? { template: opts.template }
: {}),
...(typeof opts.body === "string" ? { body: opts.body } : {}),
force: Boolean(opts.force),
},
);

const withIds = parseWithCapabilities(opts.with);
for (const id of withIds) {
if (id !== "checks") {
throw new UsageError(
`Unknown --with capability '${id}'. Available: checks.`,
);
}
}
const addedChecks = withIds.includes("checks")
? await addChecksDir(result.paths.packageDir)
: undefined;
Expand All @@ -65,7 +78,12 @@ export function registerInitCommand(cli: CAC): void {
dir: result.paths.dir,
written: result.written,
...(addedChecks !== undefined
? { checks: { written: addedChecks.written } }
? {
checks: {
written: addedChecks.written,
skipped: addedChecks.skipped,
},
}
: {}),
},
null,
Expand All @@ -84,6 +102,9 @@ export function registerInitCommand(cli: CAC): void {
for (const file of addedChecks.written) {
process.stdout.write(` checks/${file}\n`);
}
for (const file of addedChecks.skipped) {
process.stdout.write(` skipped checks/${file}\n`);
}
}
}
process.exit(0);
Expand Down
1 change: 1 addition & 0 deletions packages/ghost/src/commands/review-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function registerReviewCommand(cli: CAC): void {
diff: opts.diff,
});
const packet = await buildReviewPacket(fingerprint, diffText, {
packageDir: paths.packageDir,
runProbes: opts.probes !== false,
cwd: process.cwd(),
});
Expand Down
86 changes: 86 additions & 0 deletions packages/ghost/src/init-payloads/median/anti-goal.median.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
description: "The model's median defaults this fingerprint refuses — gather for any greenfield visual surface or first-draft copy. Each rule is reject→replace; delete lines your brand legitimately violates."
---

This is the model's median, not your brand. Each rule is reject→replace.
Delete every line your brand legitimately violates — `ghost validate` will
warn on any check reference the deletion orphans — delete the paired flag too.

These are not aesthetic opinions. Where a count is given, it is the measured
convergence of 300 unsteered generations across three frontier models (the
antimedian experiment): the defaults a model reaches for when nobody hands it
a brand. An output showing several of these tells reads as generated,
whatever else it does right. A deterministic floor rides alongside these
rules in the paired check — mechanical tells like gradient-filled text,
emoji as interface icons, em-dash pileups, and aphoristic cadence —
verified at review, never steered in prose.

### Hover-lift
Reject hover-lift (`translateY` + growing shadow) as the default interaction
→ confirm with color and background change at the fingerprint's fast
duration. (measured: 341)

### Indigo accent
Reject the indigo/blue/purple default accent (`#4f46e5`, `#2563eb`,
`#8b5cf6` family; purple/violet hue 260–310) → the fingerprint's declared
palette; absent one, monochrome plus a single functional accent.

### Dark theme
Reject unprompted dark theme → the fingerprint's declared surface.
Dark-to-look-cool and light-to-be-safe are the same retreat from a decision.
(measured: 271)

### Gradients
Reject gradient page and section backgrounds and gradient-filled CTAs → flat
surfaces in semantic roles.

### Side-stripe
Reject side-stripe borders (a thick colored border on one side of a rounded
card) — the single most recognizable tell of AI-generated UI → a full
hairline border, a 4–8% surface tint, or a leading glyph.

### Cream surface
Reject the cream/sand/beige default surface (warm off-white; token names
like `--cream`, `--sand`, `--parchment` are the tell) → a true off-white,
the brand's own hue, or a committed color; warmth via accent and type, not
the ground.

### Glassmorphism
Reject glassmorphism (decorative backdrop blur) → flat surfaces with real
borders and the fingerprint's elevation tiers.

### Chat bubbles
Reject chat bubbles with initials-circle avatars for assistant turns →
assistant text plain on the page surface; user turns marked compactly.

### Stock copy
Reject stock template copy ("Simple, transparent pricing") → headings that
state what this product specifically does.

### Celebration
Reject celebration copy and UI (exclamation success, confetti language) →
quiet factual confirmation.

### Nested cards
Reject nested cards and everything-in-cards → one surface level per region;
interior hierarchy from spacing and type; spacing and alignment group
without card overhead.

### Hero metric
Reject the hero-metric template (big number + small label + supporting stats
as default proof) → evidence specific to the product, or nothing. A
prominent metric showing real user data is fine.

### Every button primary
Reject every button a primary button → one primary per view; the rest step
down the fingerprint's emphasis ladder.

### Eyebrow kicker
Reject an eyebrow kicker on every section → at most one, where the register
sanctions it; one named kicker is voice, every-section is AI grammar.

### Decorative motion
Reject decorative looping animation (pulse/float/shimmer) and uniform
fade-and-rise on every scrolled section → motion as evidence of state
change; loops only for genuine loading; decoration never compensates for
weak structure — fix spacing, copy, and hierarchy first.
Loading